前言:什么是中间件
服务器在收到 HTTP 请求后会对用户的请求进行一系列的处理,比如检查请求的身份验证信息、处理请求报文头、检查是否存在对应的服务器端响应缓存、找到和请求对应的控制器类中的操作方法等,当控制器类中的操作方法执行完成后,服务器也会对响应进行一系列的处理,比如保存响应缓存、设置缓存报文头、设置 CORS 报文头、压缩响应内容等。这些就是通过中间件进行处理的。
广义上来讲,中间件指的是系统软件和应用软件之间连接的软件,以便于软件之间的沟通,比如 Web 服务器、Redis 服务器等都可以称作中间件
狭义上来讲,ASP.NET Core 中的中间件则指 ASP.NET Core 中的一个组件。
每个中间件由前逻辑、next、后逻辑3部分组成,前逻辑为第一段要执行的逻辑代码,next为指向下一个中间件的调用,后逻辑为从下一个中间件返回所执行的逻辑代码。
每个 HTTP 请求都要经历一系列中间件的处理,每个中间件对请求进行特定的处理后,再将其转到下一个中间件,最终的业务逻辑代码执行完成后,响应的内容也会按照请求处理的相反顺序进行处理,然后形成 HTTP 响应报文返回给客户端。
中间件组成一个管道(pipeline),整个 ASP.NET Core 的执行过程就是 HTTP 请求和响应按照中间件组装的顺序在中间件之间流转的过程。
中间件有点类似过过滤器,但中间件处理更底层,更快被处理
中间件有 3 重要的概念:Map、Use 和 Run。
Map 用来定义一个管道可以处理哪些请求,Use 和 Run 用来定义管道,一个管道由若干个 Use 和一个 Run 组成,每个 Use 引入一个中间件,而 Run 用来执行最终的核心应用逻辑。
也就是说,Map 是用来引入请求的,请求来到管道之后,由组成管道的多个 Use 负责对请求进行预处理及请求处理完成后的扫尾工作,Run 负责主要的业务规则。
下面通过一个简单的 Step By Step 例子来直观地感受中间件的魅力。
Step By Step 步骤
-
创建一个空 ASP.NET Core webapi 项目
-
打开 Program.cs,编写以下代码,留意注释
var builder = WebApplication.CreateBuilder(args); var app = builder.Build();//Map 引用请求,一个管道 app.Map("/test", async appbuilder => {//Use 中间件1appbuilder.Use(async (context, next) => {//前逻辑context.Response.ContentType = "text/html";await context.Response.WriteAsync("1 Start<br/>");//执行下一个中间件await next.Invoke(); //后逻辑await context.Response.WriteAsync("1 End<br/>");});//Use 中间件2appbuilder.Use(async (context, next) => {//前逻辑await context.Response.WriteAsync("2 Start<br/>");//执行最终业务操作方法,因为没有下一个中间件await next.Invoke(); //后逻辑await context.Response.WriteAsync("2 End<br/>");});//Run 最终业务操作方法appbuilder.Run(async ctx => {// 如果在一个中间件中使用 ctx.Response.WriteAsync 向客户端发送响应,// 就不能再执行 next.Invoke 把请求转到其他中间件了// 因为其他中间件中有可能对 Response 进行了更改,比如修改响应状态码、修改报文头或者向响应报文中写入其他数据,// 这样就会造成响应报文体被损坏的问题// 所以这里在向报文体中写入内容后,又执行 next.Invoke 是不推荐的行为// 只是为了演示而已await ctx.Response.WriteAsync("hello middleware <br/>");}); });app.Run();
-
运行,打印出执行顺序:
- 中间件1 - 前逻辑
- 中间件2 - 前逻辑
- 最终业务操作方法
- 中间件2 - 后逻辑
- 中间件1 - 后逻辑
- 输出
总结
- 不在 Map 定义的管道中的中间件(代码如: app.UseHttpsRedirection();),会默认处理所有的请求
- 中间件是按照顺序执行的,因此中间件组装的顺序非常重要