FilterAttribute(ASP.NET Core MVC 过滤器特性)

重点:ASP.NET Core 里没有直接叫 FilterAttribute 的抽象基类 ,是一类过滤器特性 的统称:ActionFilterAttribute / ExceptionFilterAttribute / ResultFilterAttribute;它们继承自 Attribute + 实现对应过滤器接口,用来标记在 Controller / Action 上,在MVC管道中横切执行逻辑(日志、权限、参数校验、异常、响应包装)。

一、5大类过滤器(管道顺序)

  1. AuthorizationFilter 授权过滤器(最先执行)
  • 接口:IAuthorizationFilter / IAsyncAuthorizationFilter
  • 作用:登录、权限校验;可以短路管道,直接返回401/403
  • 没有内置 AuthorizationFilterAttribute,一般自定义特性或用 [Authorize]

2.ResourceFilter 资源过滤器(授权之后、模型绑定之前)

    • 接口:IResourceFilter / IAsyncResourceFilter
    • 场景:缓存、限流、提前短路;比ActionFilter更早

3.ActionFilter 动作过滤器(最常用,Action前后)

    • 基类:ActionFilterAttribute(同步)
    • 4个方法:
      • OnActionExecuting:Action执行之前
      • OnActionExecuted:Action执行之后
      • OnActionExecutionAsync:异步版本(优先用异步)
    • 可修改Action参数、修改返回结果、记录耗时、入参日志

4.ResultFilter 结果过滤器(Action执行完、渲染Result前后)

    • 基类:ResultFilterAttribute
    • OnResultExecuting:结果渲染前(序列化、返回视图前)
    • OnResultExecuted:结果渲染完成后
    • 场景:统一包装返回体、添加响应头、修改序列化结果

5.ExceptionFilter 异常过滤器(发生未捕获异常时触发)

    • 基类:ExceptionFilterAttribute
    • OnException / OnExceptionAsync
    • 只在抛出异常时跑;全局统一异常处理、返回友好错误JSON

补充:Razor Page 用 IPageFilter,ActionFilter 不支持Razor Page Handler

二、执行范围 & 优先级(Scope)

作用域从小到大:Action方法 → Controller控制器 → Global全局 执行顺序(before):全局 → 控制器 → 方法 执行顺序(after):方法 → 控制器 → 全局(栈式,先进后出)

同级别多个过滤器:用 Order 属性控制,数字越小越先执行

csharp 复制代码
[MyFilter(Order = 1)]

三、短路(Short-circuit)

在Filter里直接给 context.Result 赋值,不再往下执行后续管道:

ini 复制代码
// 授权过滤器里直接拦截
context.Result = new UnauthorizedResult();

异步过滤器:不调用 await next() 就是短路

四、依赖注入问题(非常高频坑)

普通 XXXFilterAttribute 是特性,由new直接实例化,不能直接构造注入DI服务(ILogger、IRepository) 两种解决方案:

  1. ServiceFilter
  • 过滤器类注册到DI容器 services.AddScoped<MyFilter>()
  • 使用:[ServiceFilter(typeof(MyFilter))]

2.TypeFilter(推荐,不用注册)

    • [TypeFilter(typeof(MyFilter))],框架自动用ObjectFactory解析DI依赖
    • 支持传参数:[TypeFilter(typeof(MyFilter), Arguments = new object[] { "abc" })]

对比:普通Attribute适合无依赖、常量参数;有依赖优先 TypeFilter

五、最简示例:ActionFilterAttribute

csharp 复制代码
public class LogFilterAttribute : ActionFilterAttribute
{
    private readonly Stopwatch _watch;
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _watch = Stopwatch.StartNew();
        base.OnActionExecuting(context);
    }
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        _watch.Stop();
        Console.WriteLine($"接口耗时:{_watch.ElapsedMilliseconds}ms");
        base.OnActionExecuted(context);
    }
}
// 使用
[LogFilter]
public IActionResult Test()
{
    return Ok();
}

六、全局注册(Program.cs)

ini 复制代码
builder.Services.AddControllers(mvc =>
{
    mvc.Filters.Add<LogFilterAttribute>(); // 全局
});

七、过滤器 vs 中间件(Middleware)

  • 中间件:在MVC管道外面,整个请求链路(静态资源、api都走),拿到HttpContext,粒度粗
  • Filter:MVC内部,只对Controller/Action生效,可以拿到Controller上下文、Action参数、ModelState,粒度细;适合业务横切(权限、返回包装、Action日志)

八、异步写法模板(推荐,项目优先异步)

arduino 复制代码
public class AsyncFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // before
        Console.WriteLine("执行前");
        var resultCtx = await next(); // 执行后续过滤器+Action
        // after
        Console.WriteLine("执行后");
    }
}
相关推荐
Jeff-Tsai2 小时前
Day 15:API 契约分层与类型注册表
.net·架构设计·erp·definition-driven
1-1=02 小时前
C# 数据结构和特殊类型
数据结构·c#·.net·.netcore
IT规划师3 小时前
Redis 数据类型全解 - Stream 深度:消费者组、Pending 队列与死信处理(十)
redis·.net
Jeff-Tsai1 天前
Day 14:业务对象的扩展点与重写时机
.net·架构设计·erp·definition-driven
IT规划师1 天前
Redis 数据类型全解 - GEO 地理空间:附近的人、外卖、打车——背后原理与精度陷阱(九)
redis·.net
追逐时光者2 天前
一款基于 .NET 开源、免费的数据库自治诊断平台
后端·.net
追逐时光者2 天前
让 GitHub Copilot 更懂你的 .NET 代码风格:一份精简实用的 Copilot Instructions
后端·.net
Jeff-Tsai2 天前
Day 13:Session 管理与生命周期
.net·架构设计·erp·definition-driven