中间件应用程序错误 修复方案_ASP.NET Core 中间件

(给DotNet加星标,提升.Net技能)

转自:花阴偷移

cnblogs.com/MrHSR/p/10307795.html

一、概述

中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件。 

每个组件:

  • 选择是否将请求传递到管道中的下一个组件;

  • 可以在管道中的下一个组件之前和之后执行工作。

请求委托用于生成请求管道。 请求委托会处理每个 HTTP 请求。

使用以下方法配置请求委托:Run,  Map, Use扩展方法。

可以将单个请求委托作为匿名方法(称为内联中间件in-line middleware) 或者可以在可重用类中定义。

这些可重用的类和内联匿名方法是中间件,也称为中间件组件。

请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。

1、Run

//将终端中间件委托添加到应用程序的请求管道中。
public static class RunExtensions
{
  public static void Run(this IApplicationBuilder app,
                              RequestDelegate handler);
}

2、Map

// 根据给定请求路径的匹配对请求管道进行分支。
public static class MapExtensions
{

  public static IApplicationBuilder   Map(this IApplicationBuilder app, PathString pathMatch,
           Action configuration);
}

3、Use

// 提供配置应用程序请求的机制    
public interface IApplicationBuilder{
  //....
  // 将中间件委托添加到应用程序的请求管道中。
  IApplicationBuilder
  Use(Func<RequestDelegate, RequestDelegate> middleware);
}

1.1、使用 IApplicationBuilder 创建中间件管道

在Startup. Configure方法中,使用IApplicationBuilder来创建中间件管理。

每一个use开头的扩展方法将一个中间件添加到IApplicationBuilder请求管道中。

使用Use扩展方法来配置请求委托。每个use的中间件类似如下声明:

public static IApplicationBuilder Use[Middleware] 
(this IApplicationBuilder app )
public static IApplicationBuilder Use[Middleware]
(this IApplicationBuilder app , Action)

ASP.NET Core 请求管道包含一系列请求委托,依次调用。 

下图演示了这一概念, 沿黑色箭头执行。

326538f8634e60c2a4572b28a8a63bd3.png

在Startup. Configure代码中,一系列use请求委托中间件如下所示:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();

委托可以决定不将请求传递给下一个委托(中间件),这就是对请求管道进行短路。通常需要短路,因为这样可以避免不必要的工作。

下面示例 是一个最简单的 ASP.NET core 应用程序,用run方法配置请求委托,设置单个委托处理处理所有请求。此案例不包括实际的请求管道。

相反,调用单个匿名函数以响应每个 HTTP 请求。并用委托终止了管道。

public class Startup
{
  public void Configure(IApplicationBuilder app){
    app.Run(async context =>
    {
       await context.Response.WriteAsync("Hello, World!");
    });
  }
}

d99a6677f793b2582c9cec140e8e36d1.png

下面示例用Use方法将多个请求委托链接在一起,next 参数表示管道中的下一个委托。 

可通过不调用 next 参数使管道短路。

app.Use(async (context, next) =>
{
 //调用下一个委托(app.run)
 await next.Invoke();
});

app.Run(async context =>
{
   await context.Response.WriteAsync("Hello, World!");
});

1.2、中间件顺序

向 Startup.Configure 方法添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此排序对于安全性、性能和功能至关重要。

以下 Startup.Configure 方法将为常见应用方案添加中间件组件:  

(1)、异常/错误处理

(2)、HTTP 严格传输安全协议

(3)、HTTPS 重定向

(4)、静态文件服务器

(5)、Cookie 策略实施

(6)、身份验证

(7)、会话

(8)、MVC

public void Configure(IApplicationBuilder app){
   if (env.IsDevelopment())
   {
       // When the app runs in the Development environment:
       //Use the Developer Exception Page to report app runtime errors.
       //Use the Database Error Page to report database runtime errors.
       app.UseDeveloperExceptionPage();
       app.UseDatabaseErrorPage();
   }
   else
   {
       // When the app doesn't run in the Development environment:
       //Enable the Exception Handler Middleware to catch exceptions
       //thrown in the following middlewares.
       //Use the HTTP Strict Transport Security Protocol (HSTS)
       //Middleware.
       app.UseExceptionHandler("/Error");
       app.UseHsts();
   }
   // Use HTTPS Redirection Middleware to redirect HTTP requests to HTTPS.
   app.UseHttpsRedirection();

   // Return static files and end the pipeline.

   app.UseStaticFiles();

   // Use Cookie Policy Middleware to conform to EU General Data
   // Protection Regulation (GDPR) regulations.

   app.UseCookiePolicy();
   // Authenticate before the user accesses secure resources.
   app.UseAuthentication();

   // If the app uses session state, call Session Middleware after Cookie
   // Policy Middleware and before MVC Middleware.

   app.UseSession();

   // Add MVC to the request pipeline.
   app.UseMvc();
}

(1)、UseExceptionHandler 是添加到管道的第一个中间件组件。 该异常处理程序中间件可捕获稍后调用中发生的任何异常。

(2)、UseStaticFiles 静态文件中间件,应该在管道的早期调用。这样它就可以处理请求和短路,而不需要遍历其余组件。静态文件中间件不提供授权检查。 它提供的任何文件,包括wwwroot下的文件,都是公开可访问的。

(3)、UseAuthentication 身份验证中间件。未经身份验证的请求不会短路,但只有在特定的Razor页面或MVC控制器操作之后,才会发生授权(和拒绝)。

1.3、Use、Run 和 Map

配置 HTTP 管道可以使用Use、Run 和 Map,但各方法针对构建的中间件作用不同:

(1)、Use[Middleware]中间件负责调用管道中的下一个中间件,也可使管道短路(即不调用 next 请求委托)。

(2)、Run[Middleware]是一种约定,一些中间件组件可能会公开在管道末端运行的Run[Middleware]方法。

(3)、Map扩展用作约定来创建管道分支, Map*创建请求管道分支是基于给定请求路径的匹配项。

public void Configure(IApplicationBuilder app, IHostingEnvironment env){
   app.Map("/Map1",HandleMapTest1);
   app.Map("/Map2", HandleMapTest2);
   //其它请求地址
   app.Run(async context =>
   {
       await context.Response.WriteAsync("Hello from non-Map delegate.

"

);
   });
}

private static void HandleMapTest1(IApplicationBuilder app){
   app.Run(async context =>
   {
       await context.Response.WriteAsync("Map Test 1");
   });
}

private static void HandleMapTest2(IApplicationBuilder app){
   app.Run(async context =>
   {
       await context.Response.WriteAsync("Map Test 2");
   });
}

8b3a8473b2eca212f262ca72b71708fd.png

Map还支持嵌套,下面的示例中,请求访问/level1/level2a 和 /level1/level2b时进行不同逻辑处理:

app.Map("/level1", level1App => {
   level1App.Map("/level2a", level2AApp => {
       // "/level1/level2a" processing
   });
   level1App.Map("/level2b", level2BApp => {
       // "/level1/level2b" processing
   });
});

1.4、MapWhen

MapWhen 基于url给定谓词的结果创建请求管道分支。 

Func 类型的任何谓词均可用于将请求映射到管道的新分支。 

在以下示例中,谓词用于检测查询字符串变量 branch 是否存在,如果存在使用新分支(HandleBranch)。

public void Configure(IApplicationBuilder app, IHostingEnvironment env){
   //Func predicate, Action configuration
   app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
   //非匹配branch其它请求地址
   app.Run(async context =>
   {
       await context.Response.WriteAsync("Hello from non-Map delegate.

"

);
   });
}

private static void HandleBranch(IApplicationBuilder app){
    app.Run(async context =>
    {
       var branchVer = context.Request.Query["branch"];
       await context.Response.WriteAsync("Map Test 1");
    });
}

5c5eec4e1d544ee72926828d8afa2f77.png

二、编写中间件    

上面演示在请求管道中使用use,map,run方法,来委托处理每个 HTTP 请求就是中间件。通常中间件会封装在类中,并且通过扩展方法公开。

下面示例是如何编写一个中间件组件。处理逻辑是该中间件通过查询字符串设置当前请求的区域性。

/// 
/// 自定义中间件实现类
///
public class RequestCultureMiddleware
{
   //using Microsoft.AspNetCore.Http
   private readonly RequestDelegate _next;

  ///
  /// 程序启动时调用
  ///
  ///
  public RequestCultureMiddleware(RequestDelegate next){
       this._next = next;
   }

   ///
  ///每个页面请求时自动调用,方法按约定命名,必需是Invoke或InvokeAsync
  ///
  ///
  ///
  public async Task InvokeAsync(HttpContext context){
       var cultureQuery = context.Request.Query["culture"];
       if (!string.IsNullOrWhiteSpace(cultureQuery))
       {
           //using System.Globalization;
           var culture = new CultureInfo(cultureQuery);
           CultureInfo.CurrentCulture = culture;
           CultureInfo.CurrentUICulture = culture;
       }
       // Call the next delegate/middleware in the pipeline
       await _next(context);
   }
}

///
/// 通过扩展方法公开中间件
///
public static class RequestCultureMiddlewareExtensions
{
   public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder){
      //在管道中添加一个use的中间件
      return builder.UseMiddleware();
   }
}public void Configure(IApplicationBuilder app){//调用中间件
   app.UseRequestCulture();
   app.Run(async (context) =>
   {await ResponseAsync(context);
   });
}private  async  Task ResponseAsync(HttpContext context){
   context.Response.ContentType = "text/html; charset=utf-8";await context.Response.WriteAsync(//打印当前显示的语言$"Hello { CultureInfo.CurrentCulture.DisplayName }"
   );
}

46ac8a2677a449491ba1fe031267c1c1.png

2.1、请求依赖项

由于中间件是在应用启动时构造的(实例),而不是在每个请求时的,因此在每个请求过程中,中间件构造函数使用的作用域生命周期服务,不会在每个请求期间与其他依赖注入类型共享。

如果必须在中间件和其他类型之间共享一个范围服务,请将这些服务添加到 Invoke 方法的签名。 

Invoke 方法可接受由 DI 填充的参数:

public class CustomMiddleware
{
   private readonly RequestDelegate _next;
   public CustomMiddleware(RequestDelegate next){
       _next = next;
   }
   // IMyScopedService is injected into Invoke
   public async Task Invoke(HttpContext httpContext, IMyScopedService svc){
       svc.MyProperty = 1000;
       await _next(httpContext);
   }
}

官方文档:ASP.NET Core 中间件

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2

推荐阅读

(点击标题可跳转阅读)

ASP.NET Core2.0 中间件

ASP.NET Core微信服务中间件

.NET Standard FreeSql v0.0.9功能预览

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

06d6356d7c0adf059bf61509cddcee71.png

喜欢就点一下「好看」呗~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值