ASP.NET Core 中间件和过滤器在架构中扮演何种角色,有何本质区别?
摘要:引言 不知道你有没有在面试中遇到过这样的问题:"中间件和过滤器的区别是什么?",或者在平时开发中思考过:"一个请求进来,ASP.NET Core 到底是怎么一步步
引言
不知道你有没有在面试中遇到过这样的问题:"中间件和过滤器的区别是什么?",或者在平时开发中思考过:"一个请求进来,ASP.NET Core 到底是怎么一步步处理它的?"
这篇文章就来聊聊,不会涉及太深的源码,主要面向初级开发者,帮你建立一个清晰的认知。
先说中间件
中间件是 ASP.NET Core 处理 HTTP 请求的基本部件,是框架本身的组成部分。
每一个请求进来后,按照 Use 的注册顺序,依次经过每一个中间件,到达终端后,再按照相反的方向依次返回。一系列中间件串联起来,就组成了 ASP.NET Core 的请求管道。这个模型有人叫"洋葱模型",也有人叫"俄罗斯套娃模型",都是一个意思。
请求 → 中间件A → 中间件B → 中间件C(终端)
响应 ← 中间件A ← 中间件B ←
中间件可以做什么呢?它收到请求后,可以在请求/响应对象上加点佐料(自己的逻辑),然后调用 next() 把请求传递给下一个中间件。如果不调用 next(),请求就在这里短路了,后面的中间件不会执行,这个中间件就成了终端中间件。
下面是一个简单示例,演示如何自定义一个记录请求耗时的中间件:
// 定义中间件类
public class TimingMiddleware
{
private readonly RequestDelegate _next;
public TimingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
// 调用 next(),请求继续往下走
await _next(context);
// next() 返回后,响应正在往回走
sw.Stop();
Console.WriteLine($"[{context.Request.Path}] 耗时:{sw.ElapsedMilliseconds}ms");
}
}
// 在 Program.cs 中注册
var app = builder.Build();
app.UseMiddleware<TimingMiddleware>(); // 注册自定义中间件
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
如果你不想单独定义一个类,也可以用 app.Use 直接写一个内联中间件:
app.Use(async (context, next) =>
{
Console.WriteLine($"请求进来了:{context.Request.Path}");
await next(); // 传递给下一个中间件
Console.WriteLine($"响应回来了:{context.Response.StatusCode}");
});
这里注册的是终端中间件,不接受 next,请求到这里就结束了,不会再往下传:
app.Run(async context =>
{
await context.Response.WriteAsync("到终点了,不继续往下走");
});
再聊过滤器
过滤器是在路由中间件内部起作用的。
请求经过中间件管道,到达路由中间件后,路由中间件确定了要调用哪个 Controller / Action。在真正执行 Action 的前后,过滤器就夹在这里介入。
用一张简单的示意图来表示:
请求进来
→ 中间件们
→ 路由中间件(确定目标 Action)
→ 过滤器们(Action 执行前后插手)
→ Action 执行
← 过滤器们
← 路由中间件
← 中间件们
响应返回
正因为过滤器处于路由之后,所以它能拿到 MVC 的上下文信息,比如当前是哪个 Action、Action 上有哪些 Attribute、ModelState 是否合法等等。这是普通中间件做不到的,因为中间件执行的时候,路由还没解析呢。
