微軟在更新.Net Core版本的時候,動做每每很大,使得每次更新版本的時候都得當心翼翼,坑實在是太多。每每是悄咪咪的移除了某項功能或者組件,或者不在支持XX方法,這就很花時間去找回須要的東西了,下面是我的在遷移.Net Core WebApi項目過程當中遇到的問題彙總:html
1. 修改*.csproj項目文件git
<TargetFramework>netcoreapp2.2</TargetFramework>
修改成
<TargetFramework>netcoreapp3.1</TargetFramework>
2 修改Programweb
public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>().ConfigureAppConfiguration((hostingContext, config) => { config.AddJsonFile($"你的json文件.json", optional: true, reloadOnChange: true); } );
修改成json
public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .ConfigureAppConfiguration((hostingContext, config)=> { config.AddJsonFile($"你的json文件.json", optional: true, reloadOnChange: true); }); });
3.1 修改Startup.ConfigureServices跨域
services.AddMvc();
修改成
services.AddControllers();
3.2 修改Startup.Configure服務器
public void Configure(IApplicationBuilder app, IHostingEnvironment env) 修改成
using Microsoft.Extensions.Hosting; public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
IHostingEnvironment在3.0以後已被標記棄用。cookie
路由配置:app
app.UseMvc(routes => { routes.MapRoute( name: "areas", template: "{area:exists}/{controller=Home}/{action=Index}/{id?}" ); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}" ); }); 修改成 app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
你覺得結束了?還沒。async
這時候你覺得結束了,興高采烈的去服務器裝好runningTime和hosting相應的版本,運行……ide
直接cmd,進入到發佈目錄,執行:
E:\你的路徑>dotnet xxx.dll
顯示詳細錯誤
而個人相應250代碼行是:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
//原文地址:http://www.javashuo.com/article/p-ayvifeqx-kg.html
搜索最新的AutoMapper根本沒更新或改變,因此不是這個組件的問題。
嘗試下載補丁Windows6.1-KB974405-x64.msu ,無果……
卸載sdk重置,無果……
修改web.config,無果……
修改應用池32位,無果……
最後,查看發佈:勾選上【刪除現有文件】,解決……
順利能夠啓動項目以後,發現有些接口:
2020-06-29 10:02:23,357 [14] ERROR System.String - 全局異常捕捉:異常:Endpoint contains CORS metadata, but a middleware was not found that supports CORS. Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).
提示很明顯,在.net core 2.2 的時候
app.UseCors();
不是須要強制在指定位置的,在3.0以後須要設置在app.UseRouting和app.UseEndpoints 之間
app.UseRouting();
//跨域
app.UseCors(one);
app.UseCors(two);
……
app.UseEndpoints(endpoints => ……
運行以後,有些接口沒有數據返回,而有些直接報錯了。緣由又是爸爸把Newtonsoft.Json移除,使用內置的System.Text.Json,因此依賴於Newtonsoft.Json的組件將不可用,那麼,只能手動添加。
Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.5
而後添加引用
public void ConfigureServices(IServiceCollection services) { services.AddControllers().AddNewtonsoftJson();
}
目前還不太建議你使用內置的序列化,由於實在太多功能或方法不支持,詳細對比請參考 https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to
基於策略受權,我想在座的加班狗都是大同小異,在2.2之前:
public class PolicyHandler : AuthorizationHandler<PolicyRequirement> { /// <summary> /// 受權方式(cookie, bearer, oauth, openid) /// </summary> public IAuthenticationSchemeProvider Schemes { get; set; } private IConfiguration _configuration; /// <summary> /// ctor /// </summary> /// <param name="configuration"></param> /// <param name="schemes"></param> /// <param name="jwtApp"></param> public PolicyHandler(IConfiguration configuration, IAuthenticationSchemeProvider schemes) { Schemes = schemes; _jwtApp = jwtApp; _configuration = configuration; } /// <summary> /// 受權處理 /// </summary> /// <param name="context"></param> /// <param name="requirement"></param> protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement) { var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; //獲取受權方式 var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (defaultAuthenticate != null) { //驗證簽發的用戶信息 var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name); if (result.Succeeded) { httpContext.User = result.Principal; //判斷是否過時 var expirationTime = DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration).Value); if (expirationTime >= DateTime.UtcNow) { //你的校驗方式 //todo context.Succeed(requirement); } else { HandleBlocked(context, requirement); } return; } } HandleBlocked(context, requirement); } /// <summary> /// 驗證失敗返回 /// </summary> private void HandleBlocked(AuthorizationHandlerContext context, PolicyRequirement requirement) { var authorizationFilterContext = context.Resource as AuthorizationFilterContext; authorizationFilterContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(new UnAuthorizativeResponse()) { StatusCode = 202 }; //不要調用 context.Fail(),設置爲403會顯示不了自定義信息,改成Accepted202,由客戶端處理,; context.Succeed(requirement); } }
而後發現升級到3.0以後,
var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext;
3.0再也不支持返回AuthorizationFilterContext,而是返回的是RouteEndpoint,這句代碼就會報錯,因此修改的方式就是注入IHttpContextAccessor,從裏面獲取HttpContext,這裏就不用演示了吧。
並修改PolicyHandler校驗失敗時候調用的方法:
/// <summary> /// 驗證失敗返回 /// </summary> private void HandleBlocked(AuthorizationHandlerContext context, PolicyRequirement requirement) { context.Fail(); }
並在Startup.ConfigureServices修改
services.AddHttpContextAccessor();
在AddJwtBearer中
.AddJwtBearer(s => { //三、添加 Jwt bearer s.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = issuer, ValidAudience = audience, IssuerSigningKey = key, //容許的服務器時間誤差的偏移量 ClockSkew = TimeSpan.FromSeconds(5), ValidateLifetime = true }; s.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { //Token 過時 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return Task.CompletedTask; }, OnChallenge = context => { context.HandleResponse(); context.Response.StatusCode = StatusCodes.Status200OK; context.Response.ContentType = "application/json"; //無受權返回自定義信息 context.Response.WriteAsync(JsonConvert.SerializeObject(new UnAuthorizativeResponse())); return Task.CompletedTask; } }; });
UnAuthorizativeResponse 是自定義返回的內容。
Startup.Configure中啓用Authentication,注意順序
app.UseRouting(); //跨域 app.UseCors(one); app.UseCors(two); …… //啓用 Authentication app.UseAuthorization(); app.UseAuthentication(); app.UseEndpoints(endpoints => ……
也必須在app.UseRouting和app.UseEndpoints 之間。
單獨封裝的HttpContext下載方法:
public static void DownLoadFile(this HttpContext context,string fileName, byte[] fileByte, string contentType = "application/octet-stream") { int bufferSize = 1024; context.Response.ContentType = contentType; context.Response.Headers.Append("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName)); context.Response.Headers.Append("Charset", "utf-8"); context.Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition"); //context.Response.Headers.Append("Access-Control-Allow-Origin", "*"); //使用FileStream開始循環讀取要下載文件的內容 using (Stream fs = new MemoryStream(fileByte)) { using (context.Response.Body) { long contentLength = fs.Length; context.Response.ContentLength = contentLength; byte[] buffer; long hasRead = 0; while (hasRead < contentLength) { if (context.RequestAborted.IsCancellationRequested) { break; } buffer = new byte[bufferSize]; //從下載文件中讀取bufferSize(1024字節)大小的內容到服務器內存中 int currentRead = fs.Read(buffer, 0, bufferSize); context.Response.Body.Write(buffer, 0, currentRead); context.Response.Body.Flush(); hasRead += currentRead; } } } }
下載的時候發現如下錯誤:Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
2020-06-29 14:18:38,898 [109] ERROR System.String - System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead. at Microsoft.AspNetCore.Server.IIS.Core.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count) at Microsoft.AspNetCore.Server.IIS.Core.WrappingStream.Write(Byte[] buffer, Int32 offset, Int32 count) at DigitalCertificateSystem.Common.Extensions.HttpContextExtension.DownLoadFile(HttpContext context, String fileName, Byte[] fileByte, String contentType) in
……
意思不運行同步操做,修改成
context.Response.Body.WriteAsync(buffer, 0, currentRead);
這才順利完成了更新。真的太坑了,不過也感受微軟的抽象化作得很好,按需引入,減小項目的冗餘。
更多升級指南請參考「孫子兵法」:https://docs.microsoft.com/zh-cn/aspnet/core/migration/22-to-30?view=aspnetcore-2.1&tabs=visual-studio
本文已獨家受權給DotNetGeek(ID:dotNetGeek)公衆號發佈