一.概述

  中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件。 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作。

  请求委托用于生成请求管道。 请求委托会处理每个 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<IApplicationBuilder> 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<T>)

    ASP.NET Core 请求管道包含一系列请求委托,依次调用。 下图演示了这一概念。 沿黑色箭头执行。

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

    下面示例用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. <p>");
});
} 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");
});
}

    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<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量 branch 是否存在,如果存在使用新分支(HandleBranch)。

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Func<HttpContext, bool> predicate, Action<IApplicationBuilder> configuration
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch); //非匹配branch其它请求地址
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
} private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync("Map Test 1");
});
}

二. 编写中间件    

  上面演示在请求管道中使用use,map,run方法,来委托处理每个 HTTP 请求就是中间件。通常中间件会封装在类中,并且通过扩展方法公开。下面示例是如何编写一个中间件组件。处理逻辑是该中间件通过查询字符串设置当前请求的区域性。

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

      

      /// <summary>
      /// 程序启动时调用
      /// </summary>
      /// <param name="next"></param>

    public RequestCultureMiddleware(RequestDelegate next)
{
this._next = next;
}     

      /// <summary>
      ///每个页面请求时自动调用,方法按约定命名,必需是Invoke或InvokeAsync
      /// </summary>
      /// <param name="context"></param>
      /// <returns></returns>

    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);
}
} /// <summary>
/// 通过扩展方法公开中间件
/// </summary>
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
{
//在管道中添加一个use的中间件
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
        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 }"
);
}

  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 = ;
await _next(httpContext);
}
}

  参考文献:

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

asp.net core 系列 15 中间件的更多相关文章

  1. ASP.NET Core系列:中间件

    1. 概述 ASP.NET Core中的中间件是嵌入到应用管道中用于处理请求和响应的一段代码. 2. 使用 IApplicationBuilder 创建中间件管道 2.1 匿名函数 使用Run, Ma ...

  2. asp.net core 系列之中间件进阶篇-编写自定义中间件(middleware)

    中间件是被用到管道(pipeline)上来处理请求(request)和响应的(response). asp.net core 本身提供了一些内置的中间件,但是有一些场景,你可能会需要写一些自定义的中间 ...

  3. asp.net core 系列之中间件基础篇(middleware)

    中间件是一种插入到管道上进行处理请求和响应的软件:每个中间件组件具有下面的功能: 选择是否把请求传递到管道上的下一个组件 可以在下一个组件的之前和之后做处理工作 请求委托(request delega ...

  4. asp.net core 系列 16 Web主机 IWebHostBuilder

    一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...

  5. 【目录】asp.net core系列篇

    随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...

  6. 【asp.net core 系列】15 自定义Identity

    0. 前言 在之前的文章中简单介绍了一下asp.net core中的Identity,这篇文章将继续针对Identity进行进一步的展开. 1. 给Identity添加额外的信息 在<[asp. ...

  7. 技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET下实现一句话木马之asmx篇 asp.net core 系列 9 环境(Development、Staging 、Production)

    黄衫女子的武功似乎与周芷若乃是一路,飘忽灵动,变幻无方,但举手抬足之间却是正而不邪,如说周芷若形似鬼魅,那黄衫女子便是态拟神仙. 这段描写出自<倚天屠龙记>第三十八回. “九阴神抓”本是& ...

  8. Ajax跨域问题及解决方案 asp.net core 系列之允许跨越访问(Enable Cross-Origin Requests:CORS) c#中的Cache缓存技术 C#中的Cookie C#串口扫描枪的简单实现 c#Socket服务器与客户端的开发(2)

    Ajax跨域问题及解决方案   目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP ...

  9. asp.net core 系列 18 web服务器实现

    一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...

随机推荐

  1. pygame学习之绘制移动的矩形

    import pygame from pygame.locals import * pygame.init() screen = pygame.display.set_mode((600, 500)) ...

  2. sql查询单表之中大于2条的数据

    SELECT COUNT(字段)AS COUNT,字段FROM 表名 GROUP BY 字段 HAVING COUNT >=

  3. Object冷知识

    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__ 语法:Object.create(proto, [propertiesObject]) prop ...

  4. C++ Json解析CJsonObject的详细使用

    引用头文件: #include <string> #include <iostream> #include <fstream> #include <casse ...

  5. iOS调用系统发送短信和邮件分享

    //发送邮件 -(void)sendMail:(NSString*)subject content:(NSString*)content{ MFMailComposeViewController*co ...

  6. DCDC设计指南二

    DCDC电源设计指导:二 这一讲以一款SOP-8封装的Synchronous Step-Down Converter(同步降压转换器)电源IC为例,讲下电源的PCB设计. 如第一讲中所说,开始设计时就 ...

  7. 有关UnrealEngine材质编辑器中的Custom节点的一些小贴士

    PS:本文写于2017.2.1日,使用版本为4.13.第二次更新时间为2017.3.15增加了四.一些材质编辑器中的奇怪的技巧: 一.前言在Unreal中材质编辑器提供了Custom节点,作为HLSL ...

  8. Autograd:自动微分

    Autograd 1.深度学习的算法本质上是通过反向传播求导数,Pytorch的Autograd模块实现了此功能:在Tensor上的所有操作,Autograd都能为他们自动提供微分,避免手动计算导数的 ...

  9. NOIP-螺旋矩阵

    题目描述 一个 n 行 n 列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵中所有格 ...

  10. 整理SpringMVC

    Spring Web MVC核心架构图: 核心架构图流程如下: 1.首先用户发送请求------->DispatcherServlet(前端控制器),前端控制器收到请求后自己不进行处理,而是委托 ...