. net core 2.2 upgrade 3.1

Time:2021-3-5

Write on the front

When Microsoft updates the. Net core version, the action is often very big, so that every time it updates the version, it has to be careful. There are too many holes. Often, a function or component is quietly removed, or XX method is no longer supported, so it takes a lot of time to retrieve what you need. The following is a summary of the problems encountered in the process of personal migration of. Net core webapi project:

Start migration

1. Modify the *. Csproj project file

<TargetFramework>netcoreapp2.2</TargetFramework>
Amend to read
<TargetFramework>netcoreapp3.1</TargetFramework>

2. Modify the 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 ($"your JSON file. JSON", optional: true, reloadonchange: true));
          }
          );

Amend to read

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 ($"your JSON file. JSON", optional: true, reloadonchange: true));
                });
        });

3.1 modification Startup.ConfigureServices

services.AddMvc();
Amend to read
services.AddControllers();

3.2 modification Startup.Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

Amend to read
using Microsoft.Extensions.Hosting;
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

Ihostingenvironment has been marked as obsolete after 3.0.

Route configuration:

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

Amend to read

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

You think it’s over? not yet.

pit

At this time, you think it’s over. Go to the server happily, install the corresponding versions of runningtime and hosting, and run

HTTP Error 500.30 – ANCM In-Process Start Failure

Directly CMD, enter the publish directory and execute:

E: Your path > dotnet xxx.dll

Show detailed errors

My corresponding code line is:


services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

Searching for the latest automapper doesn’t update or change at all, so it’s not a problem with this component.

Try to download the patchWindows6.1-KB974405-x64.msuNo result

Uninstall SDK reset, no result

modify web.config No result

Modify application pool 32 bits, no result

Finally, view Publishing: check delete existing file to solve

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

After successfully starting the project, some interfaces are found:

2020-06-29 10:02:23,357 [14] ERROR System.String -Global exception capture: 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(…).

It’s obvious that in. Net core 2.2


app.UseCors();

It doesn’t need to be forced in the specified location. After 3.0, it needs to be set in the specified location app.UseRouting and app.UseEndpoints between

app.UseRouting (); // cross domain
app.UseCors(one);
app.UseCors(two);
……
app.UseEndpoints(endpoints => ……

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

After running, some interfaces do not return data, while some directly report errors. The reason is that my father put me back Newtonsoft.Json Remove, use built-in System.Text.Json So it depends on Newtonsoft.Json Components of will not be available, so you can only add them manually.


Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.5

Then add the reference


public void ConfigureServices(IServiceCollection services)
{
  services.AddControllers().AddNewtonsoftJson();
}

At present, it is not recommended that you use built-in serialization, because too many functions or methods are not supported. Please refer to the detailed comparisonhttps://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to

Authorization related

Based on strategic empowerment, I think the overtime dogs here are similar. Before 2.2:

public class PolicyHandler : AuthorizationHandler<PolicyRequirement>
  {
    /// <summary>
    ///Authorization method (cookie, bear, 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>
    ///Authorization processing
    /// </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;

      //How to obtain authorization
      var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
      if (defaultAuthenticate != null)
      {
        //Verify signed user information
        var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
        if (result.Succeeded)
        {
          httpContext.User = result.Principal;
         
          //Judge whether it is overdue or not
          var expirationTime = DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration).Value);
          if (expirationTime >= DateTime.UtcNow)
          {
             //How do you check
             //todo
            context.Succeed(requirement);
          }
          else
          {
            HandleBlocked(context, requirement);
          }
          return;
        }
      }
      HandleBlocked(context, requirement);
    }
     
    /// <summary>
    ///Verification failure return
    /// </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 };
      //Do not call context.Fail (), set to 403, the user-defined information will not be displayed. Instead, it will be accepted 202, which will be processed by the client,;
      context.Succeed(requirement);
    }
  }

After upgrading to 3.0,


var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext;

3.0 no longer supports the return of authorizationfiltercontext. Instead, it returns routeendpoint. This code will report an error, so the way to modify it is to inject ihttpcontextaccessor and get httpcontext from it. There is no need to demonstrate here.

And modify the method called when the policyhandler verification fails:

/// <summary>
    ///Verification failure return
    /// </summary>
    private void HandleBlocked(AuthorizationHandlerContext context, PolicyRequirement requirement)
    {
      context.Fail();
    }

And in Startup.ConfigureServices modify


 services.AddHttpContextAccessor();

In addjwtbearer

.AddJwtBearer(s =>
      {
        //3. Add JWT bear 
        s.TokenValidationParameters = new TokenValidationParameters
        {
          ValidIssuer = issuer,
          ValidAudience = audience,
          IssuerSigningKey = key,
          //Offset of allowed server time deviation
          ClockSkew = TimeSpan.FromSeconds(5),
          ValidateLifetime = true
        };
        s.Events = new JwtBearerEvents
        {
          OnAuthenticationFailed = context =>
          {
            //Token expired 
            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";
            //No authorization to return custom information
            context.Response.WriteAsync(JsonConvert.SerializeObject(new UnAuthorizativeResponse()));
            return Task.CompletedTask;
          }
        };
      });

Unauthorized response is the content returned by the custom.

Startup.Configure Enable authentication in, and pay attention to the order

app.UseRouting();
//Cross domain
app.UseCors(one);
app.UseCors(two);
……
//Enable authentication 
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints => ……

It has to be in the app.UseRouting and app.UseEndpoints between.

File download

Separately encapsulated httpcontext download method:

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", "*");
      //Use the file stream to start the loop to read the contents of the file to be downloaded
      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];
            //Read the buffer size (1024 bytes) from the download file into the server memory
            int currentRead = fs.Read(buffer, 0, bufferSize);
            context.Response.Body.Write(buffer, 0, currentRead);
            context.Response.Body.Flush();
            hasRead += currentRead;
          }
        }
      }
    }

The following error was found during download: synchronous operations are disallowed. Call writeasync or set allowsynchronous IO 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 ……

Do not run synchronization operation


context.Response.Body.WriteAsync(buffer, 0, currentRead);

This completes the update successfully. It’s really boring, but I also feel that Microsoft’s abstraction is very good. It is introduced on demand to reduce the redundancy of the project.

Please refer to “art of war” for more upgrade guides:https://docs.microsoft.com/zh-cn/aspnet/core/migration/22-to-30?view=aspnetcore-2.1&tabs=visual-studio

Author: Eminem JK
source: https://www.cnblogs.com/EminemJK/

This article about. Net core 2.2 upgrade 3.1 to avoid the pit Guide (summary) is introduced here. For more related. Net core 2.2 upgrade 3.1 content, please search the previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!