用ActionFilterAttribute是最稳妥起点,但须显式设Order、避免构造函数注入HttpContext、不可依赖“加了就生效”;它支持特性标记、构造函数注入服务且自动注册,而IActionFilter需手动注册否则静默失效。

直接说结论:用 ActionFilterAttribute 是最稳妥的起点,但必须显式设 Order、避免构造函数注入 HttpContext、且不能靠“加了就生效”来赌运气。
为什么 ActionFilterAttribute 比直接实现 IActionFilter 更常用
它自带属性标记能力(比如 [MyLogFilter]),支持构造函数注入服务,且默认注册进 DI 容器后就能被识别。而纯接口实现必须手动调用 services.AddScoped<myfilter>()</myfilter>,漏注册就完全静默失效——OnActionExecuting 根本不会触发,连日志都看不到。
常见错误现象:
- 写了
public class MyFilter : IActionFilter,但没在Program.cs里注册 → 过滤器形同虚设 - 写了
[MyFilter],但类不是public或没有无参构造函数 → 运行时报InvalidOperationException: No constructor for type xxx - 想在过滤器里直接
new HttpClient()或读取HttpContext.Request.Headers→ 构造函数里拿不到,因为HttpContext是scoped生命周期
Order 值不设等于白设
很多人以为 options.Filters.Add<logfilter>()</logfilter> 和 options.Filters.Add<authfilter>()</authfilter> 的添加顺序就是执行顺序,其实不是。所有未指定 Order 的过滤器默认都是 0,而 Order 相同时,后添加的反而先执行。
所以如果你要外层记日志、内层做鉴权,就得这样写:
options.Filters.Add<LogFilter>(10); // 先执行 options.Filters.Add<AuthFilter>(20); // 后执行
注意两点:
-
Order只在同一类型过滤器中起作用(比如两个IActionFilter);IAuthorizationFilter总是比IActionFilter先跑,不管 Order 多大 - 控制器或 Action 上标注的特性过滤器,默认
Order = -1,优先级高于全局注册的同类型过滤器——混用时务必全部显式设值,否则顺序靠试
在 OnActionExecuting 和 OnActionExecuted 里安全读取请求/响应
别在构造函数里试图存 HttpContext,也别在 OnActionExecuting 里直接调用 response.Content.ReadAsStringAsync().Result(会死锁)。正确姿势是:
- 用
context.HttpContext.RequestServices.GetService<IConfiguration>()按需解析服务(适用于全局注册的 singleton 过滤器) - 记录参数:用
actionContext.ActionArguments,它是IDictionary<string, object>,可直接序列化 - 记录响应体:在
OnActionExecuted中,先检查actionExecutedContext.Response?.Content是否非空,再用await response.Content.ReadAsStringAsync(),别用.Result - 测耗时:用
Stopwatch,把实例存在request.Properties里,避免跨方法传参出错
最容易被忽略的一点:ActionFilterAttribute 的子类如果带参数构造函数(比如 public MyFilter(string tag)),那它就只能用于特性标记([MyFilter("api")]),不能走全局注册——因为 DI 容器无法推断字符串参数来源。全局场景下,要么用无参构造 + 属性注入,要么改用接口实现 + 手动注册。


















