.Net Core 2.2升級3.1的避坑指南

来源:https://www.cnblogs.com/EminemJK/archive/2020/07/10/13206747.html
-Advertisement-
Play Games

寫在前面 微軟在更新.Net Core版本的時候,動作往往很大,使得每次更新版本的時候都得小心翼翼,坑實在是太多。往往是悄咪咪的移除了某項功能或者組件,或者不在支持XX方法,這就很花時間去找回需要的東西了,下麵是個人在遷移.Net Core WebApi項目過程中遇到的問題彙總: 開始遷移 1. 修 ...


 寫在前面

  微軟在更新.Net Core版本的時候,動作往往很大,使得每次更新版本的時候都得小心翼翼,坑實在是太多。往往是悄咪咪的移除了某項功能或者組件,或者不在支持XX方法,這就很花時間去找回需要的東西了,下麵是個人在遷移.Net Core WebApi項目過程中遇到的問題彙總:

開始遷移

1. 修改*.csproj項目文件

<TargetFramework>netcoreapp2.2</TargetFramework>

修改為

<TargetFramework>netcoreapp3.1</TargetFramework>

2 修改Program

        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);
                   }
                   );

修改為

        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之後已被標記棄用。

路由配置:

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?}");
            });

你以為結束了?還沒。

  這時候你以為結束了,興高采烈的去伺服器裝好runningTime和hosting相應的版本,運行……

HTTP Error 500.30 – ANCM In-Process Start Failure

  直接cmd,進入到發佈目錄,執行:

E:\你的路徑>dotnet xxx.dll

顯示詳細錯誤

而我的相應250代碼行是:

services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
//原文地址:https://www.cnblogs.com/EminemJK/p/13206747.html

搜索最新的AutoMapper根本沒更新或改變,所以不是這個組件的問題。

嘗試下載補丁Windows6.1-KB974405-x64.msu ,無果……

卸載sdk重置,無果……

修改web.config,無果……

修改應用池32位,無果……

最後,查看發佈:勾選上【刪除現有文件】,解決……

Endpoint contains CORS metadata, but a middleware was not found that supports CORS.

  順利可以啟動項目之後,發現有些介面:

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.UseRoutingapp.UseEndpoints 之間

app.UseRouting();
//跨域 app.UseCors(one); app.UseCors(two); ……
app.UseEndpoints(endpoints => ……

The JSON value could not be converted to System.Int32. Path……

  運行之後,有些介面沒有數據返回,而有些直接報錯了。原因又是爸爸把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);
        }
    }
View Code

然後發現升級到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 =>
            {
                //3、添加 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.UseRoutingapp.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)公眾號發佈


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ​ TensorFlow™是一個基於數據流編程(dataflow programming)的符號數學系統,被廣泛應用於各類機器學習(machine learning)演算法的編程實現,其前身是谷歌的神經網路演算法庫DistBelief 。 Tensorflow擁有多層級結構,可部署於各類伺服器、PC終端 ...
  • ​ 最近黃小斜開始接觸了一些演算法方面的開工作,於是趕緊找了好些機器學習方面的書籍來看學習,從研發到演算法,果然是完全不一樣的感覺呀。如果你寫代碼寫膩了,也不妨來學習一下演算法方面的知識,從機器學習開始,打開AI學習之路的大門吧。 機器學習實戰系列書單 ​ 機器學習實戰 機器學習是人工智慧研究領域中一個極 ...
  • ​ 最近公司里有一些關於演算法方面的工作,想到能學點有趣的新技術,於是毫不猶豫地參加了學習,機器學習,深度學習,離我們Java工程師到底遠不遠,說近不近,說遠也不遠,我們甚至可以在沒有太多機器學習理論的基礎時,去學習一些深度學習的簡單應用,至少拿到demo過來跑一下還是沒什麼問題的。 深度學習到底是啥 ...
  • ​ 機器學習是一門多領域交叉學科,涉及概率論、統計學、逼近論、凸分析、演算法複雜度理論等多門學科。專門研究電腦怎樣模擬或實現人類的學習行為,以獲取新的知識或技能,重新組織已有的知識結構使之不斷改善自身的性能。 它是人工智慧的核心,是使電腦具有智能的根本途徑。 2020年,似乎沒有哪一個方向能比機器 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 今天在升級下載Python第三方庫的時候特別慢,最後去升級pip的時候竟然還time out了,哇心態炸了。 最後想了一下為什麼會這麼慢? 因為python預設的國外的資源, ...
  • 數字統計 題目描述 請統計某個給定範圍[L,R]的所有整數中,數字2出現的次數。 比如給定範圍[2,22],數字2在數2中出現了1次,在數21中出現1次,在數22中出現2次,所以數字2在該範圍內一共出現了6次。 輸入格式 2個正整數L和R,之間用一個空格隔開。 輸出格式 數字2出現的次數。 樣例輸入 ...
  • 閑談設計模式 Intro 設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類的、代碼設計經驗的總結。 瞭解這些前輩們總結出來的經驗有助於幫助你寫出來更優秀的代碼,幫助你寫出可擴展、可讀、可維護的高質量代碼。 在極客時間里推出了數據結構和設計模式的王爭說了一句話,如果說“ ...
  • 龍芯團隊從2019年7 月份開始著手.NET Core的MIPS64支持研發,經過將近一年的研發,在2020年6月18日完成了里程碑性的工作,在github CoreCLR 倉庫:https://github.com/gsvm/coreclr, 隨後受到.NET社區的很大參與熱情鼓舞之下,2020年... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...