C#过滤器:原理、类型与实战应用详解
2026/9/18 8:41:57 网站建设 项目流程

1. 过滤器在C#中的核心价值

在.NET开发中,过滤器(Filter)就像手术室里的无菌操作流程——它能在特定环节自动拦截请求,进行预处理或后处理。我接手过的一个电商项目就曾因为缺少订单接口的权限校验,导致严重的安全漏洞。后来通过实现AuthorizationFilter,只用20行代码就修复了这个隐患。

过滤器本质上是一种面向切面编程(AOP)的实现,它允许我们将横切关注点(如日志、缓存、验证)与业务逻辑解耦。与中间件(Middleware)相比,过滤器更贴近MVC/WebAPI的请求生命周期,能精确控制Action级别的行为。

2. 五大过滤器类型深度解析

2.1 AuthorizationFilter:安全守卫

这是最先执行的过滤器,常用于权限验证。我在金融项目中实现的动态权限系统就继承自AuthorizeAttribute:

public 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 执行顺序控制

过滤器的默认执行顺序是:

  1. AuthorizationFilter
  2. ResourceFilter
  3. ActionFilter.OnActionExecuting
  4. 控制器方法
  5. ActionFilter.OnActionExecuted
  6. ResultFilter
  7. ExceptionFilter

通过Order属性可以调整顺序:

[MyFilter(Order = 1)] [MyFilter(Order = 2)] public class HomeController : Controller

3.2 依赖注入的正确姿势

过滤器支持构造函数依赖注入,但需要改用ServiceFilter或TypeFilter:

[ServiceFilter(typeof(LoggingFilter))] public IActionResult Get() { //... } // 在Startup中注册 services.AddScoped<LoggingFilter>();

3.3 动态过滤器方案

对于需要运行时判断的场景,可以实现IFilterFactory:

public class DynamicFilterFactory : IFilterFactory { public bool IsReusable => false; public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { var context = serviceProvider.GetService<IHttpContextAccessor>(); return context.HttpContext.Request.Path.StartsWith("/api") ? new ApiFilter() : new MvcFilter(); } }

4. 性能优化与避坑指南

4.1 常见性能陷阱

  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(); }
  2. 过度日志:在高频接口的过滤器中记录完整请求体会显著影响性能

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 List<IFilterMetadata>()); filter.OnAuthorization(context); Assert.IsInstanceOf<UnauthorizedResult>(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%,同时保证了数据安全性。

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

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

立即咨询