.NET Core WebAPI 中间件与过滤器(Filter)深度剖析:从原理到实战
2026/6/25 14:25:41 网站建设 项目流程

.NET Core WebAPI 中间件与过滤器(Filter)深度剖析:从原理到实战

你是否曾经困惑过:中间件和过滤器都能处理HTTP请求,它们到底有什么区别?什么时候该用中间件,什么时候该用过滤器?本文将带你彻底搞清楚这两个概念,并通过实际案例让你轻松掌握。

写在前面

作为.NET开发者,我们每天都在和HTTP请求打交道。在ASP.NET Core中,处理请求有两个重要的概念:中间件(Middleware)过滤器(Filter)。很多初学者甚至一些有经验的开发者,对它们的区别和使用场景都存在模糊认识。

记得我刚接触ASP.NET Core时,也曾被这两个概念搞晕。经过多年实践,我总结了一套通俗易懂的理解方式。今天,就让我用最直白的语言,配合实际案例,帮你彻底搞懂它们。

一、概念解析

1. 中间件(Middleware)

中间件是ASP.NET Core请求处理管道中的一个个"关卡"。每个中间件都可以:

  • 在请求到达后续中间件之前执行一些操作
  • 决定是否将请求传递给下一个中间件
  • 在后续中间件处理完成后执行一些操作

可以把它想象成一个洋葱模型

请求 → [中间件1] → [中间件2] → [中间件3] → 处理程序 ← [中间件3] ← [中间件2] ← [中间件1] ← 响应

2. 过滤器(Filter)

过滤器是MVC框架层面的概念,它依附于控制器和Action方法。过滤器允许你在Action执行的不同阶段插入自定义逻辑。

ASP.NET Core提供了五种类型的过滤器:

过滤器类型执行时机
Authorization Filter授权验证,最早执行
Resource Filter资源处理前后(模型绑定之前/之后)
Action FilterAction方法执行前后
Exception Filter异常处理
Result Filter结果执行前后

二、核心区别

为了让你更清晰地理解,我整理了一个对比表格:

对比维度中间件过滤器
所属层级管道级别(全局)MVC框架级别
作用范围所有请求特定Controller/Action
访问上下文只能访问HttpContext可以访问MVC上下文(ModelState、Action参数等)
适用场景通用HTTP处理(认证、日志、静态文件等)业务逻辑处理(参数验证、结果格式化等)
执行顺序按注册顺序按过滤器类型和Order属性
是否依赖MVC

一句话总结

中间件管"管道",过滤器管"业务"

  • 中间件:处理所有进入管道的请求,不管你是访问API还是静态文件
  • 过滤器:只处理MVC相关的请求(Controller/Action),关注业务逻辑的横切关注点

三、深入源码分析

中间件的工作原理

中间件的本质是一个委托链。看一下UseMiddleware的简化实现:

publicclassApplicationBuilder{privatereadonlyList<Func<RequestDelegate,RequestDelegate>>_components=new();publicIApplicationBuilderUse(Func<RequestDelegate,RequestDelegate>middleware){_components.Add(middleware);returnthis;}publicRequestDelegateBuild(){RequestDelegateapp=context=>{context.Response.StatusCode=404;returnTask.CompletedTask;};// 反向构建委托链foreach(varcomponentin_components.AsEnumerable().Reverse()){app=component(app);}returnapp;}}

每个中间件都接收一个RequestDelegate(下一个中间件),并返回一个新的RequestDelegate

过滤器的执行流程

过滤器的执行依赖于MVC的ActionInvoker。当请求到达MVC中间件后,路由匹配到对应的Controller和Action,然后ActionInvoker会:

  1. 执行Authorization Filter
  2. 执行Resource Filter(OnResourceExecuting)
  3. 执行Action Filter(OnActionExecuting)
  4. 执行Action方法
  5. 执行Action Filter(OnActionExecuted)
  6. 执行Result Filter(OnResultExecuting)
  7. 执行结果
  8. 执行Result Filter(OnResultExecuted)
  9. 执行Resource Filter(OnResourceExecuted)

四、实战案例

案例1:全局异常处理

使用中间件实现:

publicclassExceptionHandlingMiddleware{privatereadonlyRequestDelegate_next;privatereadonlyILogger<ExceptionHandlingMiddleware>_logger;publicExceptionHandlingMiddleware(RequestDelegatenext,ILogger<ExceptionHandlingMiddleware>logger){_next=next;_logger=logger;}publicasyncTaskInvokeAsync(HttpContextcontext){try{await_next(context);}catch(Exceptionex){_logger.LogError(ex,"请求处理发生异常");context.Response.StatusCode=StatusCodes.Status500InternalServerError;context.Response.ContentType="application/json";varresponse=new{Success=false,Message="服务器内部错误",TraceId=context.TraceIdentifier};awaitcontext.Response.WriteAsync(JsonSerializer.Serialize(response));}}}// 注册方式app.UseMiddleware<ExceptionHandlingMiddleware>();

使用过滤器实现:

publicclassGlobalExceptionFilter:IExceptionFilter{privatereadonlyILogger<GlobalExceptionFilter>_logger;publicGlobalExceptionFilter(ILogger<GlobalExceptionFilter>logger){_logger=logger;}publicvoidOnException(ExceptionContextcontext){_logger.LogError(context.Exception,"Action执行发生异常");context.Result=newObjectResult(new{Success=false,Message=context.Exception.Message,// 开发环境可以返回具体信息StackTrace=context.Exception.StackTrace}){StatusCode=StatusCodes.Status500InternalServerError};context.ExceptionHandled=true;}}// 注册方式builder.Services.AddControllers(options=>{options.Filters.Add<GlobalExceptionFilter>();});

对比分析:

  • 中间件的异常处理更底层,会捕获所有请求(包括静态文件)的异常
  • 过滤器的异常处理只针对MVC请求,但可以访问MVC上下文,比如获取Action参数

案例2:请求/响应日志记录

使用中间件实现:

publicclassRequestLoggingMiddleware{privatereadonlyRequestDelegate_next;privatereadonlyILogger<RequestLoggingMiddleware>_logger;publicRequestLoggingMiddleware(RequestDelegatenext,ILogger<RequestLoggingMiddleware>logger){_next=next;_logger=logger;}publicasyncTaskInvokeAsync(HttpContextcontext){// 记录请求信息varrequest=context.Request;varrequestBody=awaitReadRequestBodyAsync(request);_logger.LogInformation("请求路径: {Path}, 方法: {Method}, Body: {Body}",request.Path,request.Method,requestBody);// 记录响应信息varoriginalBodyStream=context.Response.Body;usingvarresponseBodyStream=newMemoryStream();context.Response.Body=responseBodyStream;await_next(context);context.Response.Body.Seek(0,SeekOrigin.Begin);varresponseBody=awaitnewStreamReader(context.Response.Body).ReadToEndAsync();context.Response.Body.Seek(0,SeekOrigin.Begin);_logger.LogInformation("响应状态码: {StatusCode}, Body: {Body}",context.Response.StatusCode,responseBody);awaitresponseBodyStream.CopyToAsync(originalBodyStream);}privateasyncTask<string>ReadRequestBodyAsync(HttpRequestrequest){request.EnableBuffering();varbody=awaitnewStreamReader(request.Body).ReadToEndAsync();request.Body.Seek(0,SeekOrigin.Begin);returnbody;}}

使用过滤器实现:

publicclassActionLoggingFilter:IActionFilter{privatereadonlyILogger<ActionLoggingFilter>_logger;publicActionLoggingFilter(ILogger<ActionLoggingFilter>logger){_logger=logger;}publicvoidOnActionExecuting(ActionExecutingContextcontext){varcontrollerName=context.Controller.GetType().Name;varactionName=context.ActionDescriptor.DisplayName;varparameters=context.ActionArguments;_logger.LogInformation("执行Action: {Controller}.{Action}, 参数: {@Parameters}",controllerName,actionName,parameters);}publicvoidOnActionExecuted(ActionExecutedContextcontext){varcontrollerName=context.Controller.GetType().Name;varactionName=context.ActionDescriptor.DisplayName;if(context.Exception!=null){_logger.LogError(context.Exception,"Action执行失败");}else{varresult=context.Result;_logger.LogInformation("Action执行成功: {Controller}.{Action}, 结果: {Result}",controllerName,actionName,result);}}}

对比分析:

  • 中间件可以记录完整的请求和响应内容(包括HTTP头、Body等)
  • 过滤器只能记录MVC层面信息,但可以访问Action参数模型,记录更业务化的日志

案例3:接口权限验证

使用中间件实现(JWT验证):

publicclassJwtAuthenticationMiddleware{privatereadonlyRequestDelegate_next;privatereadonlyIConfiguration_configuration;publicJwtAuthenticationMiddleware(RequestDelegatenext,IConfigurationconfiguration){_next=next;_configuration=configuration;}publicasyncTaskInvokeAsync(HttpContextcontext){// 白名单路径跳过验证varpath=context.Request.Path.Value;if(path=="/api/auth/login"||path=="/api/auth/register"){await_next(context);return;}vartoken=context.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ","");if(string.IsNullOrEmpty(token)){context.Response.StatusCode=StatusCodes.Status401Unauthorized;awaitcontext.Response.WriteAsync("缺少认证Token");return;}try{// 验证JWT Token...await_next(context);}catch(Exception){context.Response.StatusCode=StatusCodes.Status401Unauthorized;awaitcontext.Response.WriteAsync("无效的Token");}}}

使用过滤器实现(自定义权限验证):

publicclassPermissionFilter:IAuthorizationFilter{privatereadonlystring_requiredPermission;publicPermissionFilter(stringrequiredPermission){_requiredPermission=requiredPermission;}publicvoidOnAuthorization(AuthorizationFilterContextcontext){varuser=context.HttpContext.User;if(!user.Identity.IsAuthenticated){context.Result=newUnauthorizedResult();return;}// 获取用户权限列表varpermissions=user.Claims.Where(c=>c.Type=="Permission").Select(c=>c.Value).ToList();if(!permissions.Contains(_requiredPermission)){context.Result=newForbidResult();return;}}}// 使用方式[PermissionFilter("User.Delete")][HttpDelete("{id}")]publicasyncTask<IActionResult>DeleteUser(intid){// 只有拥有User.Delete权限的用户才能执行}

对比分析:

  • 中间件适合做统一的认证(如JWT验证),针对所有请求
  • 过滤器适合做细粒度的授权,可以针对不同的Action配置不同的权限要求

五、最佳实践指南

什么时候使用中间件?

适合中间件的场景:

  • 跨所有请求的通用处理(认证、日志、异常捕获)
  • 处理静态文件
  • 压缩/加密请求和响应
  • 跨域处理(CORS)
  • 请求限流
  • 自定义路由

不适合中间件的场景:

  • 需要访问MVC特定功能(ModelState、Action参数)
  • 需要根据Controller/Action做差异化处理
  • 需要访问Action的返回值

什么时候使用过滤器?

适合过滤器的场景:

  • Action参数的验证和预处理
  • Action执行前后的业务逻辑
  • 统一的结果格式化
  • 细粒度的权限控制
  • 缓存控制
  • 事务管理(如EF Core的事务)

不适合过滤器的场景:

  • 处理非MVC请求(如静态文件)
  • 需要在整个管道层面做处理
  • 处理原始HTTP请求/响应流

执行顺序的注意事项

注册顺序很重要!举个实际例子:

// Program.csvarapp=builder.Build();// 1. 异常处理中间件(最先注册,最后执行)app.UseMiddleware<ExceptionHandlingMiddleware>();// 2. 认证中间件app.UseMiddleware<AuthenticationMiddleware>();// 3. 日志中间件app.UseMiddleware<LoggingMiddleware>();// 4. MVC中间件(最后注册,最先执行)app.MapControllers();app.Run();

执行顺序:日志 → 认证 → 异常处理 → 请求 → 异常处理(返回)→ 认证(返回)→ 日志(返回)

六、进阶技巧

1. 中间件和过滤器的结合使用

实际项目中,我通常这样组合使用:

// 中间件处理:全局异常、JWT认证、请求日志app.UseMiddleware<ExceptionHandlingMiddleware>();app.UseMiddleware<JwtAuthenticationMiddleware>();app.UseMiddleware<RequestLoggingMiddleware>();// 过滤器处理:权限验证、参数校验、事务管理builder.Services.AddControllers(options=>{options.Filters.Add<PermissionFilter>();options.Filters.Add<ValidationFilter>();options.Filters.Add<TransactionFilter>();});

2. 自定义中间件的最佳实践

// 推荐:使用扩展方法简化注册publicstaticclassMiddlewareExtensions{publicstaticIApplicationBuilderUseCustomAuth(thisIApplicationBuilderapp){returnapp.UseMiddleware<CustomAuthMiddleware>();}}// 使用app.UseCustomAuth();

3. 依赖注入的区别

  • 中间件:通过构造函数注入,生命周期为Singleton或Scoped(Invoke方法参数可以获取Scoped服务)
  • 过滤器:可以通过ServiceFilter或TypeFilter属性注入,支持更灵活的DI
// 中间件获取Scoped服务publicasyncTaskInvokeAsync(HttpContextcontext,IUserServiceuserService){// userService 是Scoped生命周期}// 过滤器使用ServiceFilter[ServiceFilter(typeof(UserService))]publicclassUserController:ControllerBase{// 自动注入}

七、总结

核心要点

  1. 本质区别:中间件在管道层面工作,过滤器在MVC框架层面工作
  2. 作用范围:中间件影响所有请求,过滤器只影响MVC请求
  3. 使用场景:中间件做"管道"的事,过滤器做"业务"的事
  4. 执行顺序:中间件按注册顺序执行,过滤器有固定的生命周期顺序

快速决策表

需求推荐方案
统一异常处理都可以,范围广用中间件,需要MVC上下文用过滤器
JWT认证中间件
细粒度权限控制过滤器(Authorization Filter)
请求日志中间件(记录原始数据)
Action参数验证过滤器(Action Filter)
跨域处理中间件
响应缓存过滤器(Result Filter)
处理静态文件中间件

一句话记忆

中间件是"管道工",负责整个HTTP管道的疏通;过滤器是"质检员",负责MVC业务环节的品质把控。


希望这篇文章能帮你彻底理解中间件和过滤器的区别。在实际开发中,选择合适的工具能让你的代码更优雅、更高效。如果还有其他疑问,欢迎在评论区交流讨论!

下期预告:我们将深入探讨如何在ASP.NET Core中实现优雅的全局事务管理,敬请期待!


如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、分享!你的支持是我持续创作的动力。🚀

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询