聊聊 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 是否合法等等。这是普通中间件做不到的,因为中间件执行的时候,路由还没解析呢。
| // 定义过滤器类,继承 ActionFilterAttribute | |
| public class ValidateModelAttribute : ActionFilterAttribute | |
| { | |
| // 在 Action 执行之前触发 | |
| public override void OnActionExecuting(ActionExecutingContext context) | |
| { | |
| if (!context.ModelState.IsValid) | |
| { | |
| // ModelState 校验不通过,直接短路,返回 400 | |
| var errors = context.ModelState | |
| .Where(e => e.Value?.Errors.Count > 0) | |
| .ToDictionary( | |
| e => e.Key, | |
| e => e.Value!.Errors.Select(x => x.ErrorMessage).ToArray() | |
| ); | |
| context.Result = new BadRequestObjectResult(new | |
| { | |
| code = 400, | |
| message = "参数校验失败", | |
| errors | |
| }); | |
| } | |
| } | |
| // 在 Action 执行之后触发 | |
| public override void OnActionExecuted(ActionExecutedContext context) | |
| { | |
| // 可以在这里统一处理响应,比如包一层统一的返回格式 | |
| } | |
| } |
使用方式很简单,直接作为 Attribute 标注在 Controller 或 Action 上:
| // 标注在单个 Action 上 | |
| [] | |
| [] | |
| public IActionResult CreateUser([FromBody] CreateUserRequest request) | |
| { | |
| // 走到这里说明 ModelState 已经通过校验 | |
| return Ok(new { message = "创建成功" }); | |
| } | |
| // 也可以标注在 Controller 上,对所有 Action 生效 | |
| [] | |
| [] | |
| public class UserController : ControllerBase | |
| { | |
| // ... | |
| } |
如果想全局生效,在 Program.cs 里注册就行:
| builder.Services.AddControllers(options => | |
| { | |
| options.Filters.Add<ValidateModelAttribute>(); // 全局注册 | |
| }); |
它俩的区别
| 中间件 | 过滤器 | |
|---|---|---|
| 作用范围 | 所有 HTTP 请求,包括静态文件 | 仅限 Controller / Action |
| 典型使用场景 | 限流、压缩、跨域、日志、认证 | 权限校验、参数校验、统一响应封装、异常处理 |
一句话总结:中间件是 HTTP 管道的基础设施,过滤器是 MVC 系的切面机制,它们不是同一层次的东西。
给你一个小技巧,不确定用哪个的时候,问自己一个问题:我需要知道当前是哪个 Action 吗? 需要的话用过滤器,不需要的话中间件就够了。
内置清单
最后整理一份 ASP.NET Core 已经提供的常用中间件和过滤器,遇到需求的时候先查查有没有现成的,别重复造轮子。
常用内置中间件:
| 中间件 | 说明 |
|---|---|
UseRouting |
路由解析 |
UseAuthentication |
认证 |
UseAuthorization |
授权 |
UseStaticFiles |
静态文件服务 |
UseCors |
跨域 |
UseExceptionHandler |
全局异常处理 |
UseHttpsRedirection |
HTTPS 重定向 |
UseResponseCompression |
响应压缩 |
UseRateLimiter |
限流(.NET 7 新增) |
内置过滤器:
| 过滤器 | 说明 |
|---|---|
[Authorize] |
授权校验 |
[AllowAnonymous] |
允许匿名访问 |
[RequireHttps] |
强制 HTTPS |
[ValidateAntiForgeryToken] |
防止 CSRF 攻击 |
[ResponseCache] |
响应缓存 |
[Produces] |
指定响应内容类型 |
[Consumes] |
指定请求内容类型 |
2026-04-14 11:34:41【出处】:https://www.cnblogs.com/denglei1024/p/19818211
=======================================================================================
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【关注我】。(●'◡'●)
因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Jack_孟】!
本文来自博客园,作者:jack_Meng,转载请注明原文链接:https://www.cnblogs.com/mq0036/p/19864357
【免责声明】本文来自源于网络,如涉及版权或侵权问题,请及时联系我们,我们将第一时间删除或更改!
浙公网安备 33010602011771号