C#过滤器:原理、类型与实战应用详解

发布时间:2026/9/18 8:42:19
C#过滤器:原理、类型与实战应用详解 1. 过滤器在C#中的核心价值在.NET开发中过滤器Filter就像手术室里的无菌操作流程——它能在特定环节自动拦截请求进行预处理或后处理。我接手过的一个电商项目就曾因为缺少订单接口的权限校验导致严重的安全漏洞。后来通过实现AuthorizationFilter只用20行代码就修复了这个隐患。过滤器本质上是一种面向切面编程AOP的实现它允许我们将横切关注点如日志、缓存、验证与业务逻辑解耦。与中间件Middleware相比过滤器更贴近MVC/WebAPI的请求生命周期能精确控制Action级别的行为。2. 五大过滤器类型深度解析2.1 AuthorizationFilter安全守卫这是最先执行的过滤器常用于权限验证。我在金融项目中实现的动态权限系统就继承自AuthorizeAttributepublic class DynamicAuthorizeAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext context) { var user context.HttpContext.User; var action context.ActionDescriptor.ActionName; if(!PermissionService.Check(user, action)) { context.Result new ForbidResult(); } } }重要提示不要在AuthorizationFilter中进行耗时操作如数据库查询这会拖慢整个请求管道。建议使用缓存策略。2.2 ActionFilter业务逻辑的瑞士军刀ActionFilter能在方法执行前后插入逻辑比如这个API耗时监控的实现public class BenchmarkFilter : IActionFilter { private Stopwatch _sw; public void OnActionExecuting(ActionExecutingContext context) { _sw Stopwatch.StartNew(); } public void OnActionExecuted(ActionExecutedContext context) { _sw.Stop(); Logger.Info($Action {context.ActionDescriptor.DisplayName} 耗时 {_sw.ElapsedMilliseconds}ms); } }实际项目中我常用它来处理自动模型验证替代Controller中的if(!ModelState.IsValid)请求数据预处理如Trim字符串参数响应数据统一包装2.3 ResultFilter最后的加工车间当需要统一修改视图结果时ResultFilter非常有用。比如这个自动包装API响应的案例public class ApiResultFilter : IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { if(context.Result is ObjectResult objResult) { context.Result new ObjectResult(new { Code 200, Data objResult.Value, Timestamp DateTimeOffset.Now.ToUnixTimeSeconds() }); } } }2.4 ExceptionFilter系统消防员全局异常处理是ExceptionFilter的典型场景。这个实现会区分业务异常和系统异常public class CustomExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { if(context.Exception is BusinessException bizEx) { context.Result new JsonResult(new { Code bizEx.ErrorCode, Message bizEx.Message }); } else { context.Result new StatusCodeResult(500); Logger.Error(context.Exception); } context.ExceptionHandled true; } }经验之谈ExceptionFilter处理不了的异常如Filter本身的异常会进入中间件异常处理建议两者配合使用。2.5 ResourceFilter高性能利器ResourceFilter可以在模型绑定前短路请求适合做缓存检查。我在高并发项目中用它实现接口缓存public class CacheResourceFilter : IResourceFilter { public void OnResourceExecuting(ResourceExecutingContext context) { var cacheKey GenerateCacheKey(context.HttpContext.Request); if(MemoryCache.TryGetValue(cacheKey, out var cachedResult)) { context.Result cachedResult; } } public void OnResourceExecuted(ResourceExecutedContext context) { if(context.Result ! null) { MemoryCache.Set(GenerateCacheKey(context.HttpContext.Request), context.Result, TimeSpan.FromMinutes(5)); } } }3. 过滤器实战进阶技巧3.1 执行顺序控制过滤器的默认执行顺序是AuthorizationFilterResourceFilterActionFilter.OnActionExecuting控制器方法ActionFilter.OnActionExecutedResultFilterExceptionFilter通过Order属性可以调整顺序[MyFilter(Order 1)] [MyFilter(Order 2)] public class HomeController : Controller3.2 依赖注入的正确姿势过滤器支持构造函数依赖注入但需要改用ServiceFilter或TypeFilter[ServiceFilter(typeof(LoggingFilter))] public IActionResult Get() { //... } // 在Startup中注册 services.AddScopedLoggingFilter();3.3 动态过滤器方案对于需要运行时判断的场景可以实现IFilterFactorypublic class DynamicFilterFactory : IFilterFactory { public bool IsReusable false; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { var context serviceProvider.GetServiceIHttpContextAccessor(); return context.HttpContext.Request.Path.StartsWith(/api) ? new ApiFilter() : new MvcFilter(); } }4. 性能优化与避坑指南4.1 常见性能陷阱同步阻塞在过滤器中调用异步方法时没有正确await会导致线程池耗尽// 错误做法 public void OnActionExecuting(ActionExecutingContext context) { _client.GetAsync(url).Result; // 同步阻塞 } // 正确做法 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { await _client.GetAsync(url); await next(); }过度日志在高频接口的过滤器中记录完整请求体会显著影响性能4.2 过滤器与中间件的抉择特性过滤器中间件执行阶段MVC管道内模型绑定后更早的请求管道阶段上下文信息丰富的MVC上下文ActionDescriptor等只有基础的HttpContext适用范围仅对Controller/Action生效全局所有请求经验法则需要MVC上下文信息 → 用过滤器需要处理静态文件请求 → 用中间件性能关键路径 → 优先考虑中间件4.3 单元测试策略测试过滤器需要模拟执行上下文推荐使用Moq框架[Test] public void AuthFilter_Should_Reject_Anonymous() { var filter new JwtAuthFilter(); var context new AuthorizationContext( new ActionContext( new DefaultHttpContext(), new RouteData(), new ActionDescriptor()), new ListIFilterMetadata()); filter.OnAuthorization(context); Assert.IsInstanceOfUnauthorizedResult(context.Result); }5. 企业级应用案例5.1 审计日志系统结合ActionFilter和DI实现的审计方案public class AuditFilter : IAsyncActionFilter { private readonly AuditDbContext _db; public AuditFilter(AuditDbContext db) _db db; public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var audit new AuditLog { UserId context.HttpContext.User.FindFirst(sub)?.Value, Action context.ActionDescriptor.DisplayName, Parameters JsonSerializer.Serialize(context.ActionArguments), Timestamp DateTime.UtcNow }; var resultContext await next(); audit.ResponseStatus context.HttpContext.Response.StatusCode; audit.IsSuccess resultContext.Exception null; await _db.AuditLogs.AddAsync(audit); await _db.SaveChangesAsync(); } }5.2 多租户数据隔离通过ResourceFilter实现租户上下文注入public class TenantFilter : IAsyncResourceFilter { public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { var tenantId context.HttpContext.Request.Headers[X-Tenant-Id]; var tenant await TenantStore.GetAsync(tenantId); if(tenant null) { context.Result new BadRequestResult(); return; } // 注入租户上下文 context.HttpContext.Items[CurrentTenant] tenant; await next(); } }在控制器中通过特性标注使用[TypeFilter(typeof(TenantFilter))] public class ProductsController : Controller这些实战经验来自我参与的SaaS平台项目过滤器方案使租户隔离代码量减少了70%同时保证了数据安全性。