商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C#怎么实现过滤器Filter_C# ASP.NET Core过滤器详解教程【进阶】

C#怎么实现过滤器Filter_C# ASP.NET Core过滤器详解教程【进阶】

  发布于2026-07-20 阅读(0)

扫一扫,手机访问

ASP.NET Core过滤器需关注执行顺序、作用域和DI生命周期;接口实现需手动注册,特性继承更常用但需满足构造函数要求;全局注册须注意scoped服务注入问题,Order值须显式设置以控制同类型过滤器执行顺序。

C#怎么实现过滤器Filter_C# ASP.NET Core过滤器详解教程【进阶】

ASP.NET Core的过滤器,很多人觉得它是个黑盒——写上去就能用,但实际远没那么简单。它的执行顺序、作用域,还有依赖注入的生命周期,这三个点但凡有一个没搞明白,过滤器就可能不触发、注入失败,或者行为完全不对。所以,想用好过滤器,先得把这几个关键点理清楚。

过滤器接口选哪个:IActionFilter vs ActionFilterAttribute

直接实现 IActionFilter 接口,灵活性是最高的,但得自己手动把它注册到DI容器里,比如 services.AddScoped()。这一步忘了,那 OnActionExecuting 方法压根不会被执行。相比之下,继承 ActionFilterAttribute 是更常见的做法——它自带属性语法,比如 [MyFilter] 就能直接用,而且在构造函数里注入依赖也是默认支持的。但要注意,这个类必须是 public 的,而且得有一个无参数构造函数,否则运行时就会报 InvalidOperationException: No constructor for type xxx 这样的错。

  • 如果想在多个 Action 上复用同一个过滤器,优先选 ActionFilterAttribute,再加上 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 来控制它到底能用在方法上还是类上。
  • 要是想统一拦截所有请求,不想依赖属性标记,那就走全局注册的路子:options.Filters.Add()。但这时候必须得实现接口,而且得把服务注册好。
  • 在过滤器里要用 HttpContextIConfiguration 吗?构造函数注入可以,但有个坑:全局注册的过滤器生命周期是 singleton,而 HttpContext 是 scoped 的,直接注入会报 Cannot resolve scoped service from root provider。正确做法是用 context.HttpContext.RequestServices.GetService() 按需解析。

Order 值怎么设才真正控制执行顺序

不少人以为 options.Filters.Add()options.Filters.Add() 的调用顺序就是执行顺序,其实不是这么回事。默认情况下,通过 Add() 注册的过滤器,Order 值都是 0——是的,你没看错,全是 0。Order 相同时,后添加的反而先执行。所以,如果你想让日志过滤器在外层、认证在内层,就得显式指定 Order 值:

options.Filters.Add(10); // 先执行
options.Filters.Add(20); // 后执行

需要特别注意的是,这个 Order 值只在同一类过滤器中生效。比如两个 IActionFilter 之间可以比较,但跨类型就不行——IAuthorizationFilter 永远比 IActionFilter 先执行,这是由框架设计决定的。

  • 控制器或 Action 上标注的属性过滤器,Order 默认是 -1,比全局注册的同类型过滤器优先级更高。
  • 如果混用了全局注册、特性标记、控制器级过滤器,建议全部显式设置 Order,避免靠“试出来”的顺序来维护。
  • Order = int.MinValueint.MaxValue 可以作为安全边界,但别用 0 当“默认值”来凑数,那样容易乱。

异常过滤器 IExceptionFilter 的常见失效场景

IExceptionFilter 只捕获操作方法内部抛出的异常。模型绑定失败、中间件异常、授权失败(比如 UnauthorizedResult)这些情况,它都不会触发。典型的失效现象是:你加上了 [CustomExceptionFilter],但参数绑定出错时,比如字符串转 int 失败了,页面还是返回 400,而过滤器的 OnException 方法根本没进去。

  • 想统一处理模型绑定错误?得用 ModelStateInvalidFilter,它继承自 IActionFilter,在方法里检查 context.ModelState.IsValid 就行。
  • 想捕获授权失败?改用 IAuthorizationFilter,在 OnAuthorization 里判断 context.Result is UnauthorizedResult
  • 想记录所有未处理异常,包括中间件抛出的?应该用 UseExceptionHandler 中间件,而不是过滤器。
  • 还有一点,context.ExceptionHandled = true 必须显式设置,否则异常会继续向上冒泡,可能被外层中间件再次处理,导致行为异常。

DI 注入时 Scoped 服务拿不到的典型错误

全局注册的过滤器默认是 singleton 生命周期,但很多业务服务,比如 IDbContextFactoryILogger,都是 scoped 的。直接通过构造函数注入,启动时就会报错:Cannot consume scoped service 'xxx' from singleton 'yyy'

  • 解决方案一:把过滤器本身也注册为 Scoped。但要注意,全局过滤器设为 scoped 后,每次请求都会新建实例,性能开销会稍微增加。
  • 解决方案二:在方法体内用 context.HttpContext.RequestServices.GetService() 来获取服务。这是最稳妥的做法,特别适合日志、配置、缓存这类轻量级依赖。
  • 千万别在 OnActionExecuting 里 new 一个 DbContext,它不会自动加入当前请求的变更跟踪,Sa veChanges 可能会静默失败,查都查不到。

最容易被忽略的一点是:过滤器的执行时机与 MVC 管道阶段是强绑定的,并不是“代码写了就运行”。比如资源过滤器的 OnResourceExecuting 在模型绑定之前执行,这时候 context.ActionArguments 还是空的;而动作过滤器的 OnActionExecuting 执行时,才能看到绑定后的参数。如果看错了阶段,就很容易对着空值做逻辑判断,结果自然不对。

本文转载于:https://www.php.cn/faq/2325028.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注