ASP.NET Core 中间件执行机制详解:Use、Run、Map 与请求管道原理

ASP.NET Core 中间件执行机制详解:Use、Run、Map 与请求管道原理
L X Y中间件
中间件是 ASP.NET Core 的核心组件,例如响应缓存、用户身份验证、CORS、Swagger等重要的功能都是由 ASP.NET 内置的中间件提供的
注:这一篇主要讲解中间件的一些简单概念,下一篇文章讲解自定义中间件。
中间件的三个概念 Map、Use、Run
处理顺序
Map 用来定义一个管道可以处理哪些请求, Use 和 Run 用来定义管道, “一个管道” 由 “若干个Use” 和 “一个Run” 组成,
每个 Use 引入一个中间件,而 Run 用来执行最终的核心应用逻辑。
上代码
1 | app.Map("/test", async appbuilder => { |
猜一下访问 /test 后的输出内容是什么(如图)?
这个顺序是不是和大家想的有点不一样。
为什么不是这个顺序
1 | 1 Start |
因为中间件是分为前、后逻辑,而前后逻辑是由 next 分开的。
awit next.Invoke(); 把请求转到下一个中间件,即不会执行该语句后面的代码(后逻辑),
直接把请求转到下一个 “use” ;
直到遇到 “run” , “ run “ 负责业务规则且不再将请求向后传递,当 “run” 执行结束后,
响应会按照请求的相反顺序执行中间件中的后逻辑。
注:如果我们在一个中间件中使用 context.Response.WriteAsync 等方式向客户端发送响应,我们就尽量不再执行 next.Invoke 把请求转到其他中间件,
因为其他中间件中有可能对 Response 中进行了修改,比如修改响应状态码、报文头、向报文中写入其他数据,这样就会造成报文体被损坏的问题。
所以正常的请求处理路径是(如图):
中间件执行顺序是:先进后出(栈结构)
执行顺序
我们现在把第一个和第二个中间件交换一下顺序。
1 | app.Map("/test", async appbuilder => { |
运行截图:
从图中我们可以看出中间件的注册顺序就是执行顺序。
然后再回到我们的代码,两个中间件除了交换了一下顺序,是否还有其他修改。
修改在这里
context.Response.ContentType = “text/html;charset=utf-8”;
回看 “处理顺序” 实验的代码部分。 这句代码在第一个中间件里面,现在中间件交换顺序后,还是在第一个中间件里面。
| 设置 | 效果 | 浏览器行为 |
|---|---|---|
| text/html; charset=utf-8 | 告诉浏览器:”这是 HTML,用 UTF-8 编码” | 正确渲染 HTML 标签,中文不乱码 |
| 不设置(默认) | 浏览器猜测内容类型 | 可能显示纯文本、乱码,或不渲染 |
为什么把context.Response.ContentType = “text/html;charset=utf-8”;放在第一个中间件。
在第一个中间件中使用 WriteAsync 方法写入body 会触发客户端响应,导致 header 被锁定
当第一次向 Response.Body 写入数据时:
- 服务器会发送响应头
- 此时 Response.HasStarted = true
- 之后再修改 Header 会抛异常
此时因为 header 已经被锁定,就无法在第二个中间件中再设置 header
如果想在第二个中间件中使用context.Response.ContentType = “text/html;charset=utf-8”; 那么需要注意不要在该中间件之前中间件不要触发客户端响应。
所以平时就尽量先设头,后写体。








