.Net Core 2.2升级3.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());

搜索最新的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.UseRouting和app.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);
    }
  }

然后发现升级到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.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

作者:EminemJK(山治先生)
出处:https://www.cnblogs.com/EminemJK/

到此这篇关于.Net Core 2.2升级3.1的避坑指南(小结)的文章就介绍到这了,更多相关.Net Core 2.2升级3.1内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Asp.net Core 1.1 升级后操作mysql出错的解决办法

    遇到问题 core的版本从1.0升级到1.1,操作mysql数据库,查询数据时遇到MissingMethodException问题,更新.插入操作没有问题. 如果你也遇到这个问题,请参照以下步骤进行升级操作. 操作步骤及内容 如果你的项目已经引用过以下几部分内容,请依次升级内容: PM> Update-Package Microsoft.EntityFrameworkCore PM> Update-Package Microsoft.EntityFrameworkCore.SqlServer

  • .Net Core 2.2升级3.1的避坑指南(小结)

    写在前面 微软在更新.Net Core版本的时候,动作往往很大,使得每次更新版本的时候都得小心翼翼,坑实在是太多.往往是悄咪咪的移除了某项功能或者组件,或者不在支持XX方法,这就很花时间去找回需要的东西了,下面是个人在迁移.Net Core WebApi项目过程中遇到的问题汇总: 开始迁移 1. 修改*.csproj项目文件 <TargetFramework>netcoreapp2.2</TargetFramework> 修改为 <TargetFramework>net

  • ASP.NET Core 3.0迁移的完美避坑指南

    一.前言 .NET Core 3.0将会在 .NET Conf 大会上正式发布,截止今日发布了9个预览版,改动也是不少,由于没有持续关注,今天将前面开源的动态WebApi项目迁移到.NET Core 3.0还花了不少时间踩坑,给大家分享一下我在迁移过程中遇到的坑.迁移的版本是当前Release最新版本 .NET Core 2.2 到 .NET Core 3.0 Preview 9. 二.ASP.NET Core 项目迁移 官方迁移文档:从 ASP.NET Core 2.2 迁移到3.0 ,这个官

  • ASP .NET Core API发布与部署以及遇到的坑和解决方法

    最近在写.Net Core的API,由于之前没接触过Core,所以自己的想法很简单,感觉和.Net都是差不多的东西,发布部署还是按.Net的那一套来,可谓是困难重重,走了不少弯路. 也没什么漂亮的词语,也不是技术大牛,就是抱着学习技术的态度,随手写写这一套流程,以及遇到的问题跟解决方法. 一.安装服务器环境 IIS  Path-->Control Panel\All Control Panel Items\Programs and Features 没细致研究,全装了~ 二.发布API 1.选择

  • 详解babel升级到7.X采坑总结

    最近工作比较忙,有一段时间没有写前端玩了.今天试着搭一个项目,发现各种坑,以前用起来非常好的配置文件各种报错.排查后发现原来babel升级了一个大版本,已经到7.X了,这里我总结一下升级过程中踩到的坑. Error: Cannot find module '@babel/core' babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core')

  • 详解升级react-router 4 踩坑指南

    一.前言 上午把近日用React做的一个新闻项目所依赖的包升级到了最新的版本,其中从react-router(2.8.1)升级到react-router(4.1.2)中出现了很多问题, 故总结一下在升级过程中遇到的问题. 二.react-router,V4版本修改内容 1. 所有组件更改为从react-router-dom导入 之前的所有路由组件均是从react-router中导入,在我之前的项目中,导入相关组件如下: //v2 import {Router,Route,hashHistory}

  • webpack 1.x升级过程中的踩坑总结大全

    前言 大家应该都知道,Webpack 是一个前端资源加载/打包工具.它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源.因为最近在对博客做SSR,无奈vue ssr demo或例子都是基于webpack2的.博主在webpack1.x上折腾了许久,vue-ssr-server-bundle.json文件生成仍然还是遥遥无期.最后还是乖乖地开始了webpack升级之旅. 本文主要记录升级过程中遇到的一些坑和解决办法,可能有些遗漏了,能记多少记多少吧.话不多说了,来一

  • Android studio2.3.3升级到3.1.2坑(小记)

    1.grade配置Error: Could not find com.android.tools.build:gradle:2.2.1. 解决方法与Maven仓库有点像:进入 D:\software\android\android-studio-ide-145.3276617-windows\android-studio\gradle\m2repository\com\android\tools\build\gradle 将项目中的build.gradle文件中 dependencies { c

  • 解决nacos升级spring cloud 2020.0无法使用bootstrap.yml的问题

    nacos升级spring cloud 2020.0无法使用bootstrap.yml 之前用spring cloud整合nacos,需要一个bootstrap.yml作为spring启动的初始化配置 bootstrap.yml内容大概如下: spring: application: # 应用名称 name: xxx profiles: active: dev cloud: nacos: config: file-extension: yml server-addr: localhost:884

  • .NET+PostgreSQL实践与避坑指南(推荐)

    简介 .NET+PostgreSQL(简称PG)这个组合我已经用了蛮长的一段时间,感觉还是挺不错的.不过大多数人说起.NET平台,还是会想起跟它"原汁原味"配套的Microsoft SQL Server(简称MSSQL),其实没有MSSQL也没有任何问题,甚至没有Windows Server都没问题,谁说用.NET就一定要上微软全家桶?这都什么年代了-- PG和MSSQL的具体比较我就不详细展开了,自行搜一下,这种比较分析文章很多.应该说两个RDBMS各有特色,MSSQL工具集庞大(大

随机推荐