1.前言

中间件(middleware)是一种装配到应用管道以处理请求和响应的组件。每个组件:
可选择是否将请求传递到管道中的下一个组件。
可在管道中的下一个组件前后执行工作。
请求委托(request delegates)用于建立请求管道(request pipeline),请求委托处理每个HTTP请求。
请求委托通过使用IApplicationBuilder类型的Run、Map和Use扩展方法来配置,并在Strartup类中传给Configure方法。每个单独的请求委托都可以被指定为一个内嵌匿名方法(称为并行中间件,in-line middleware),或者其定义在一个可重用的类中。这些可重用的类被称作“中间件”或“中间件组件”。请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。当中间件短路时,它被称为“终端中间件”(terminal middleware),因为它阻止中间件进一步处理请求。

2.使用 IApplicationBuilder 创建中间件管道

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

每个委托(中间件)均可在下一个委托前后执行操作。任何委托都能选择停止传递到下一个委托,转而自己处理该请求,这就是请求管道的短路(下面会举例说明)。而且是一种有意义的设计,因为它可以避免不必要的工作。比如,一个授权(authorization)中间件只有通过身份验证之后才能调用下一个委托,否则它就会被短路,并返回“Not Authorized”的响应。所以应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。
现在我们来演示下用一个简单的ASP.NET Core应用程序建立单个请求委托处理每个HTTP请求(这种情况不包括实际请求管道):

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

响应结果:

由上面我们可以看到,运行时输出的是Run委托消息,然后我们再定义多一个请求委托看看效果,请看如下代码:

public void Configure(IApplicationBuilder app){//第一个委托Run    app.Run(async context =>    {await context.Response.WriteAsync("Hello, World!");    });//第二个委托Run    app.Run(async context =>    {await context.Response.WriteAsync("Hey, World!");    });}

响应结果:

由上述代码可以看到,我们定义两个Run委托,但是运行第一个Run委托的时候就已经终止了管道,这是为什么呢?
因为Run方法又称为短路管道(它不会调用next请求委托)。因此,Run方法一般在管道尾部被调用。Run是一种约定,有些中间件组件可能会暴露他们自己的Run方法,而这些方法只能在管道末尾处运行。

让我们再来看看如下代码:

public void Configure(IApplicationBuilder app){    app.Use(async (context, next) =>    {        context.Response.ContentType = "text/plain; charset=utf-8";await context.Response.WriteAsync("进入第一个委托 执行下一个委托之前\r\n");//调用管道中的下一个委托await next.Invoke();await context.Response.WriteAsync("结束第一个委托 执行下一个委托之后\r\n");    });    app.Run(async context =>    {await context.Response.WriteAsync("进入第二个委托\r\n");await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");await context.Response.WriteAsync("结束第二个委托\r\n");    });}

响应结果:

通过响应结果,我们可以看到Use方法将多个请求委托链接在一起。而next参数表示管道中的下一个委托。可通过不调用next 参数使管道短路,通常可在下一个委托前后执行操作。

3.顺序

向Startup.Configure方法添加中间件组件的顺序定义了在请求上调用它们的顺序,以及响应的相反顺序。此排序对于安全性、性能和功能至关重要。
以下Startup.Configure方法将为常见应用方案添加中间件组件:
●异常/错误处理(Exception/error handling)
●HTTP严格传输安全协议(HTTP Strict Transport Security Protocol)
●HTTPS重定向(HTTPS redirection)
●静态文件服务器(Static file server)
●Cookie策略实施(Cookie policy enforcement)
●身份验证(Authentication)
●会话(Session)
●MVC
请看如下代码:

从上述示例代码中,每个中间件扩展方法都通过Microsoft.AspNetCore.Builder命名空间在 IApplicationBuilder上公开。但是为什么我们要按照这个顺序去添加中间件组件呢?下面我们挑几个中间件来了解下。
UseExceptionHandler(异常/错误处理)是添加到管道的第一个中间件组件。因此我们可以捕获在应用程序调用中发生的任何异常。那为什么要将异常/错误处理放在第一位呢?那是因为这样我们就不用担心因前面中间件短路而导致捕获不到整个应用程序所有异常信息。
UseStaticFiles(静态文件)中间件在管道中提前调用,方便它可以处理请求和短路,而无需通过剩余中间组件。也就是说静态文件中间件不用经过UseAuthentication(身份验证)检查就可以直接访问,即可公开访问由静态文件中间件服务的任何文件,包括wwwroot下的文件。
UseAuthentication(身份验证)仅在MVC选择特定的Razor页面或Controller和Action之后才会发生。
经过上面描述,大家都了解中间件顺序的重要性了吧。以下示例演示中间件的排序,其中静态文件的请求在响应压缩中间件之前由静态文件中间件进行处理。静态文件不会按照中间件的顺序进行压缩。可以压缩来自 UseMvcWithDefaultRoute的 MVC 响应。示例:

public void Configure(IApplicationBuilder app){// Static files not compressed by Static File Middleware.    app.UseStaticFiles();    app.UseResponseCompression();    app.UseMvcWithDefaultRoute();}

4.Use、Run和Map方法

你可以使用Use、Run和Map配置HTTP管道。
●Use:Use方法可使管道短路(即不调用 next 请求委托)。第二节点有示例代码演示。
●Run:Run是一种约定,并且某些中间件组件可公开在管道末尾运行的Run[Middleware]方法。第二节点有示例代码演示。
●Map:Map扩展用作创建管道分支。Map*给请求路径的匹配项来创建请求管道分支。如果请求路径以给自定义路径开头,则执行分支。
下面我们来看看这段代码:

public class Startup{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");        });    }public void Configure(IApplicationBuilder app)    {        app.Map("/map1", HandleMapTest1);        app.Map("/map2", HandleMapTest2);        app.Run(async context =>        {await context.Response.WriteAsync("Hello from non-Map delegate. <p>");        });    }}

下面表格使用前面的代码显示来自http://localhost:5001的请求和响应。

请求

响应

localhost:5001

Hello from non-Map delegate.

localhost:5001/map1

Map Test 1

localhost:5001/map2

Map Test 2

localhost:5001/map3

Hello from non-Map delegate.

由上面可以了解到当使用Map方法时,将从HttpRequest.Path中删除匹配的路径段,并针对每个请求将该路径追加到HttpRequest.PathBase。
MapWhen基于给定谓词的结果创建请求管道分支。Func<HttpContext, bool>类型的任何谓词均可用于将请求映射到管道的新分支(HandleBranch)。在以下示例中,谓词用于检测查询字符串变量branch是否存在:

public class Startup{private static void HandleBranch(IApplicationBuilder app)    {        app.Run(async context =>        {var branchVer = context.Request.Query["branch"];await context.Response.WriteAsync($"Branch used = {branchVer}");        });    }public void Configure(IApplicationBuilder app)    {        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),                               HandleBranch);        app.Run(async context =>        {await context.Response.WriteAsync("Hello from non-Map delegate. <p>");        });    }}

下面表格使用前面的代码显示来自http://localhost:5001的请求和响应。

请求

响应

http://localhost:5001

Hello from non-Map delegate. <p>

https://localhost:5001/?branch=master

Branch used = master

Map支持嵌套,例如:

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

此外Map 还可同时匹配多个段:

public class Startup{private static void HandleMultiSeg(IApplicationBuilder app)    {        app.Run(async context =>        {await context.Response.WriteAsync("Map multiple segments.");        });    }public void Configure(IApplicationBuilder app)    {        app.Map("/map1/seg1", HandleMultiSeg);        app.Run(async context =>        {await context.Response.WriteAsync("Hello from non-Map delegate.");        });    }}

5.编写中间件(重点)

虽然ASP.NET Core为我们提供了一组丰富的内置中间件组件,但在某些情况下,你可能需要写入自定义中间件。

5.1中间件类

通常,中间件应该封装在自定义类中,并且通过扩展方法公开。
下面我们自定义一个查询当前区域性的中间件:

public class Startup{public void Configure(IApplicationBuilder app)    {        app.Use((context, next) =>        {var cultureQuery = context.Request.Query["culture"];if (!string.IsNullOrWhiteSpace(cultureQuery))            {var culture = new CultureInfo(cultureQuery);                CultureInfo.CurrentCulture = culture;                CultureInfo.CurrentUICulture = culture;            }// Call the next delegate/middleware in the pipelinereturn next();        });        app.Run(async (context) =>        {await context.Response.WriteAsync(                $"Hello {CultureInfo.CurrentCulture.DisplayName}");        });    }}

可通过传入区域性参数测试该中间件。例如 http://localhost:7997/?culture=zh、http://localhost:7997/?culture=en。
但是为了更好管理代码,我们应该把委托函数移到自定义类去:

//自定义RequestCultureMiddleware类public class RequestCultureMiddleware{private readonly RequestDelegate _next;public RequestCultureMiddleware(RequestDelegate next)    {        _next = next;    }public async Task InvokeAsync(HttpContext context)    {        context.Response.ContentType = "text/plain; charset=utf-8";var cultureQuery = context.Request.Query["culture"];if (!string.IsNullOrWhiteSpace(cultureQuery))        {var culture = new CultureInfo(cultureQuery);            CultureInfo.CurrentCulture = culture;            CultureInfo.CurrentUICulture = culture;        }// Call the next delegate/middleware in the pipelineawait _next(context);    }}

5.2中间件扩展方法

中间件扩展方法可以通过IApplicationBuilder公开中间件。示例创建一个RequestCultureMiddlewareExtensions扩展类并通过IApplicationBuilder公开:

public static class RequestCultureMiddlewareExtensions{public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)    {return builder.UseMiddleware<RequestCultureMiddleware>();    }}

再通过Startup.Configure方法调用中间件:

public class Startup{public void Configure(IApplicationBuilder app)    {        app.UseRequestCulture();        app.Run(async (context) =>        {await context.Response.WriteAsync(                $"Hello {CultureInfo.CurrentCulture.DisplayName}");        });    }}

响应结果:

由此整个自定义ASP.NET Core中间件完成。

6.按请求依赖项

因为中间件是在应用程序启动时构建的,而不是每个请求时构建,所以在每个请求期间,中间件构造函数使用的范围内生命周期服务不与其他依赖关系注入类型共享。如果您必须在中间件和其他类型之间共享作用域服务,请将这些服务添加到Invoke方法的签名中。Invoke方法可以接受由依赖注入(DI)填充的其他参数。示例:

public class CustomMiddleware{private readonly RequestDelegate _next;public CustomMiddleware(RequestDelegate next)    {        _next = next;    }// IMyScopedService is injected into Invokepublic async Task Invoke(HttpContext httpContext, IMyScopedService svc)    {        svc.MyProperty(1000);await _next(httpContext);    }}public static class CustomMiddlewareExtensions{public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)    {return builder.UseMiddleware<CustomMiddleware>();    }}public interface IMyScopedService{void MyProperty(decimal input);}public class MyScopedService : IMyScopedService{public void MyProperty(decimal input)    {        Console.WriteLine("MyProperty is " + input);    }}public void ConfigureServices(IServiceCollection services){//注入DI服务    services.AddScoped<IMyScopedService, MyScopedService>();}

响应结果:

参考文献:
ASP.NET Core中间件
写入自定义ASP.NET Core中间件

原文地址:https://www.cnblogs.com/wzk153/p/10904988.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

ASP.NET Core 中间件相关推荐

  1. ASP.NET Core 中间件(Middleware)详解

    ASP.NET Core 中间件(Middleware)详解 原文:ASP.NET Core 中间件(Middleware)详解 本文为官方文档译文,官方文档现已非机器翻译 https://docs. ...

  2. ASP.NET Core中间件初始化探究

    前言 在日常使用ASP.NET Core开发的过程中我们多多少少会设计到使用中间件的场景,ASP.NET Core默认也为我们内置了许多的中间件,甚至有时候我们需要自定义中间件来帮我们处理一些请求管道 ...

  3. ASP.NET Core 中间件分类

    ASP.NET Core 中间件的配置方法可以分为以上三种,对应的Helper方法分别是:Run(), Use(), Map(). Run(),使用Run调用中间件的时候,会直接返回一个响应,所以后续 ...

  4. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  5. ASP.NET Core 中间件Diagnostics使用

    ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...

  6. ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

    ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...

  7. ASP.NET Core 中间件Diagnostics使用 异常和错误信息

    ASP.NET Core 中间件Diagnostics使用 异常和错误信息 参考文章: (1)ASP.NET Core 中间件Diagnostics使用 异常和错误信息 (2)https://www. ...

  8. 【netcore】 ASP.NET Core 中间件

    基本概念 中间件是一种装配到应用管道以处理请求和响应的软件. 每个组件: 选择是否将请求传递到管道中的下一个组件. 可在管道中的下一个组件前后执行工作. 请求委托用于生成请求管道. 请求委托处理每个 ...

  9. ASP.NET Core中间件实现分布式 Session(转载)

    ASP.NET Core中间件实现分布式 Session 1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件 ...

最新文章

  1. C++_复合、委托、继承
  2. Beta 冲刺(5/7)
  3. 基于JSP实现的项目管理平台系统
  4. 简介Linux磁盘管理与文件系统
  5. 洛谷 P2324 [SCOI2005]骑士精神 解题报告
  6. 学习笔记(43):Python实战编程-事件处理简介
  7. matlab读取图片的频率,获得时域图之后,也获得了频域图,但是如何查看频率呢......
  8. stats | 线性回归(四)——显著性检验和模型评价
  9. 移动硬盘无法访问,要怎么找到文件
  10. shell制表与脚本运行进度条写法
  11. hadoop资料汇总
  12. 埃默里大学有计算机专业吗,埃默里大学计算机专业怎么样?
  13. C++搜索与回溯算法之红与黑
  14. Android项目:基于安卓Android校园零食配送系统app(计算机毕业设计)
  15. java有理数类的封装_【Java笔记】有理数的类封装
  16. win10系统添加打印机
  17. 判断标题不为空(包含空格)
  18. 图解HTTP+彩色版 pdf版学习(更新中)
  19. 提取视频中的音频——python三行程序搞定
  20. 浙江大学计算机学院 耿卫东教授 是哪个实验室的,文化与科技的碰撞:2018首届中国文化计算大会在京举行...

热门文章

  1. 荣耀9igoogle模式_iGoogle个性化主页的6种替代方法
  2. C#自定义字符串压缩和解压缩源码库
  3. Tomcat定义虚拟主机案例
  4. 探索发现:平台云——云的新风向
  5. [图] DevOps:提速从研发到交付流程
  6. mysql数据库的备份和二进制日志恢复
  7. Windows下MinGW编译vim7.4
  8. 【转载】分布式系统的理解
  9. 2006年4月全国计算机等级考试二级Java语言程序设计
  10. 【C#/.NET】不用AutoMapper,我用啥呢?