在ASP.NET Core MVC中,过滤器提供了在请求处理管道的不同阶段插入横切逻辑的机制。资源筛选器通常用于处理缓存、防盗链、资源初始化或者任何需要在模型绑定之前决定是否继续执行管道的逻辑。与其他筛选器相比,资源筛选器的关键特征是它在模型绑定之前运行,并且可以通过设置上下文中的Result属性来短路整个管道。这意味着一旦检测到缓存命中,Action方法根本不会执行,模型绑定也不会发生,服务端可以立即返回缓存内容。

资源筛选器在过滤器管道中的执行时机
ASP.NET Core MVC将过滤器分为多个阶段,执行顺序从外到内依次为授权筛选器、资源筛选器、模型绑定、Action筛选器、Action执行、异常筛选器、结果筛选器、结果执行。资源筛选器处于授权筛选器之后,但在模型绑定之前。这一位置赋予它两个重要能力:一是能够在模型绑定消耗资源之前拦截请求;二是能够像授权筛选器一样短路请求。
需要明确的是,资源筛选器的短路行为与授权筛选器并不冲突。授权筛选器负责身份验证和授权决策,通常在资源筛选器之前运行;资源筛选器更适合处理与资源本身相关的预处理逻辑,比如根据请求路径读取缓存、检查If-None-Match请求头,或者初始化一个必须贯穿后续Action执行的共享对象。理解执行顺序有助于避免在资源筛选器中重复读取请求体,因为模型绑定还没有开始,此时读取请求体可能干扰后续Action的参数绑定。
从生命周期来看,资源筛选器在Action执行前触发一次,在Action执行后触发一次。同步接口通过OnResourceExecuting和OnResourceExecuted两个方法实现;异步接口通过一个包含委托的方法实现。无论使用哪种方式,资源筛选器都能访问完整的ResourceExecutingContext和ResourceExecutedContext,并可通过它们获取HttpContext、路由数据、模型状态以及Result等信息。
同步与异步接口的实现方式
ASP.NET Core为资源筛选器提供了两个接口:IResourceFilter和IAsyncResourceFilter。同步接口适合逻辑简单、不涉及异步IO的场景,异步接口适合需要访问数据库、分布式缓存或外部HTTP服务的场景。两个接口可以同时实现,但框架会优先使用异步接口。开发时应根据实际需要只实现其中一个,避免同时实现导致执行顺序混乱。
下面是实现同步资源筛选器的完整示例。该类通过构造函数注入ILogger,并且在执行前记录日志,在执行后清理资源。代码中的ResourceExecutingContext和ResourceExecutedContext对象由框架创建并传入。
public class LoggingResourceFilter : IResourceFilter
{
private readonly ILogger<LoggingResourceFilter> _logger;
public LoggingResourceFilter(ILogger<LoggingResourceFilter> logger)
{
_logger = logger;
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
_logger.LogInformation("资源筛选器开始执行,请求路径:{Path}", context.HttpContext.Request.Path);
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
_logger.LogInformation("资源筛选器执行完成,是否存在异常:{HasException}", context.Exception != null);
}
}
异步资源筛选器使用ResourceExecutionDelegate委托来调用管道中的下一个阶段。调用next()之前编写执行前逻辑,调用之后编写执行后逻辑。需要注意的是,如果决定短路管道,就不应再调用next(),否则仍然会继续执行后续阶段。
public class ShortCircuitResourceFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
if (context.HttpContext.Request.Headers.ContainsKey("X-Skip-Action"))
{
context.Result = new ContentResult
{
Content = "请求已被资源筛选器短路,Action不会执行。",
ContentType = "text/plain",
StatusCode = 200
};
return;
}
await next();
}
}
这个示例展示了资源筛选器最经典的用法:执行前检查请求条件,命中则设置Result并直接返回;未命中则继续执行后续管道。由于资源筛选器在模型绑定之前执行,这种短路操作可以完全避免模型绑定和Action执行的开销。
注册资源筛选器的几种方式
创建好的资源筛选器必须注册到MVC管道中才能生效。ASP.NET Core提供了四种常见的注册方式:通过特性标注到Controller或Action、通过TypeFilter特性、通过ServiceFilter特性、以及通过全局过滤器配置。
如果筛选器不需要依赖注入,可以直接以特性形式使用。例如定义一个继承自Attribute并实现IResourceFilter的类,然后标注在Controller或Action上。但如果筛选器需要通过构造函数注入服务,就不能简单使用特性实例化,因为特性参数必须是编译期常量。这时候需要使用TypeFilter或ServiceFilter。
[TypeFilter(typeof(LoggingResourceFilter))]
public class ProductsController : Controller
{
public IActionResult Index()
{
return View();
}
}
TypeFilter会根据注册的服务创建筛选器实例,并将服务容器中的依赖注入到构造函数中。它不需要预先在服务容器中注册筛选器类型。ServiceFilter与之类似,但要求筛选器类型必须预先在Startup或Program文件中注册。全局注册则适用于所有请求都需要经过的资源筛选器。
builder.Services.AddControllers(options =>
{
options.Filters.Add<LoggingResourceFilter>();
});
在实际项目中,如果需要按特定顺序执行多个资源筛选器,可以通过实现IOrderedFilter接口并设置Order属性来控制顺序。资源筛选器的执行顺序还受注册位置影响,全局筛选器先于Controller和Action上的筛选器执行。理解这些细节可以避免缓存逻辑和日志逻辑之间出现意外干扰。
短路响应与典型应用场景
资源筛选器最强大的特性之一就是短路响应。当OnResourceExecuting或OnResourceExecutionAsync的执行前逻辑设置了context.Result,后续的模型绑定、Action执行和Result执行都会被跳过,MVC会直接执行Result对象来生成响应。这意味着可以在请求到达业务代码之前就返回结果,非常适合缓存命中、频繁访问的静态资源处理、防盗链验证以及限流等场景。
除了缓存,资源筛选器还适合处理本地化设置。例如在请求进入Action之前读取语言偏好cookie或请求头,将CultureInfo写入当前线程,从而让后续的数据格式化和视图渲染使用正确的语言环境。也可以利用资源筛选器的一次性资源包装能力,在执行前打开数据库连接、创建日志作用域,在执行后释放非托管资源。
需要注意的是,资源筛选器虽然灵活,但不应滥用。如果设置短路响应后仍然有部分逻辑需要执行,应考虑使用结果筛选器或中间件。资源筛选器与中间件在功能上有一定重叠,但资源筛选器运行在MVC框架内部,能够使用路由数据和Action上下文等信息,这是中间件不具备的优势。根据具体场景选择合适的扩展点,可以保证请求管道清晰且性能可控。
ASP.NET Core资源筛选器IResourceFilter修改时间:2026-08-29 22:04:26