1. ASP.NET Core Filters核心机制解析
在ASP.NET Core MVC框架中,Filters是拦截请求处理管道的轻量级组件,它们像安检关卡一样在特定阶段对HTTP请求进行拦截处理。与中间件(Middleware)不同,Filters更专注于MVC动作方法的执行上下文,提供了五个标准接口供我们实现特定拦截逻辑:
csharp复制public interface IAuthorizationFilter { void OnAuthorization(AuthorizationFilterContext context); }
public interface IResourceFilter { void OnResourceExecuting(ResourceFilterContext context); void OnResourceExecuted(ResourceFilterContext context); }
public interface IActionFilter { void OnActionExecuting(ActionExecutingContext context); void OnActionExecuted(ActionExecutedContext context); }
public interface IExceptionFilter { void OnException(ExceptionContext context); }
public interface IResultFilter { void OnResultExecuting(ResultExecutingContext context); void OnResultExecuted(ResultExecutedContext context); }
每个接口对应不同的执行阶段:
- IAuthorizationFilter:最早执行的过滤器,用于权限验证
- IResourceFilter:资源处理前后触发,适合缓存实现
- IActionFilter:动作方法执行前后介入
- IExceptionFilter:捕获处理管道中的异常
- IResultFilter:视图结果生成前后执行
重要提示:实际开发中更常用的是它们的异步版本接口(如IAsyncAuthorizationFilter),这些接口在性能敏感场景下表现更好。
2. 过滤器执行顺序深度剖析
2.1 默认执行流程
当请求进入ASP.NET Core管道时,过滤器的执行顺序遵循严格的生命周期:
- 全局过滤器(通过AddMvcOptions配置)
- 控制器级别过滤器(使用[TypeFilter]等特性标注)
- 动作方法级别过滤器(方法上的特性)
每个层级内部的执行顺序为:
code复制授权过滤器(Authorization) → 资源过滤器(Resource) → 模型绑定 →
动作过滤器(Action) → 动作方法执行 →
动作过滤器(Action)后置处理 →
异常过滤器(Exception) →
结果过滤器(Result) →
结果过滤器(Result)后置处理
2.2 顺序控制技巧
通过Order属性可以精确控制同类型过滤器的执行顺序:
csharp复制[TypeFilter(typeof(CustomFilter), Order = 1)]
[ServiceFilter(typeof(AnotherFilter), Order = 2)]
public IActionResult Index()
{
return View();
}
实测发现:Order值越小优先级越高,未设置Order时默认为0。当Order相同时,执行顺序为全局→控制器→方法。
3. 依赖注入在Filters中的高级应用
3.1 服务注入的三种方式
方式1:ServiceFilterAttribute(推荐)
csharp复制// 注册服务
services.AddScoped<IDemoService, DemoService>();
services.AddScoped<DemoActionFilter>();
// 控制器使用
[ServiceFilter(typeof(DemoActionFilter))]
public IActionResult Get()
{
return Ok();
}
方式2:TypeFilterAttribute
csharp复制[TypeFilter(typeof(CustomFilter), Arguments = new object[] { "参数1" })]
public IActionResult Test()
{
return View();
}
方式3:IFilterFactory实现
csharp复制public class CustomFilterFactory : IFilterFactory
{
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var logger = serviceProvider.GetService<ILogger<CustomFilter>>();
return new CustomFilter(logger);
}
}
3.2 生命周期注意事项
- 避免在Singleton过滤器中使用Scoped服务:这会导致服务被不正确地共享
- 资源型过滤器适合实现缓存:因为它们在模型绑定前执行
- 动作过滤器中的服务释放:框架会自动处理Scoped服务的生命周期
4. 实战中的过滤器开发技巧
4.1 性能监控过滤器实现
csharp复制public class BenchmarkFilter : IActionFilter
{
private Stopwatch _stopwatch;
public void OnActionExecuting(ActionExecutingContext context)
{
_stopwatch = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_stopwatch.Stop();
var elapsed = _stopwatch.ElapsedMilliseconds;
var logger = context.HttpContext.RequestServices
.GetService<ILogger<BenchmarkFilter>>();
logger.LogInformation($"执行耗时:{elapsed}ms");
}
}
4.2 全局异常处理最佳实践
csharp复制public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is BusinessException ex)
{
context.Result = new JsonResult(new {
Code = ex.ErrorCode,
Message = ex.Message
});
context.ExceptionHandled = true;
}
}
}
// 注册为全局过滤器
services.AddMvc(options => {
options.Filters.Add<CustomExceptionFilter>();
});
5. 常见问题排查指南
5.1 过滤器不执行的可能原因
-
注册方式错误:
- 确保ServiceFilter对应的类型已注册
- TypeFilter需要有无参构造函数或参数能通过DI解析
-
执行顺序冲突:
- 检查是否有Order值更大的同类型过滤器提前终止了管道
-
MVC未正确配置:
- 确认已调用
services.AddControllersWithViews()
- 确认已调用
5.2 DI服务获取为null的解决方案
csharp复制// 错误方式(构造函数注入在特性中无效)
public class BadFilter : IActionFilter
{
private readonly ILogger _logger; // 始终为null
public BadFilter(ILogger logger) { _logger = logger; }
}
// 正确方式(通过HttpContext获取)
public void OnActionExecuting(ActionExecutingContext context)
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger>();
}
6. 高级应用场景
6.1 动态过滤器配置
结合配置系统实现运行时开关:
csharp复制public class FeatureToggleFilter : IActionFilter
{
private readonly IConfiguration _config;
public FeatureToggleFilter(IConfiguration config)
{
_config = config;
}
public void OnActionExecuting(ActionExecutingContext context)
{
var featureName = context.ActionDescriptor.DisplayName;
if (!_config.GetValue<bool>($"Features:{featureName}"))
{
context.Result = new StatusCodeResult(404);
}
}
}
6.2 过滤器与中间件协同工作
典型的分工方案:
- 中间件处理跨领域基础功能(如HTTPS重定向)
- 过滤器处理MVC特定逻辑(如模型验证)
mermaid复制graph TD
A[中间件管道] --> B[路由中间件]
B --> C[授权中间件]
C --> D[MVC过滤器管道]
D --> E[授权过滤器]
E --> F[资源过滤器]
F --> G[动作过滤器]
7. 性能优化建议
- 避免过度使用过滤器:每个过滤器都会增加管道复杂度
- 优先使用异步版本:IAsyncResultFilter比IResultFilter性能更好
- 缓存资源过滤器结果:适合不变的数据响应
- 慎用全局过滤器:会影响所有请求
我在实际项目中发现,合理使用资源过滤器可以将某些查询接口的吞吐量提升40%以上。例如对商品分类这种不常变动的数据,可以在资源过滤器中实现缓存逻辑:
csharp复制public class CategoryCacheFilter : IResourceFilter
{
private static readonly ConcurrentDictionary<string, object> _cache
= new ConcurrentDictionary<string, object>();
public void OnResourceExecuting(ResourceExecutingContext context)
{
var cacheKey = context.HttpContext.Request.Path;
if (_cache.TryGetValue(cacheKey, out var data))
{
context.Result = new JsonResult(data);
}
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
if (context.Result is JsonResult result && result.Value != null)
{
var cacheKey = context.HttpContext.Request.Path;
_cache.TryAdd(cacheKey, result.Value);
}
}
}
