ASP.NET Core 应用 - Filters
参考资料:
过滤器类型
每个过滤器类型都将在管道的不同阶段被执行:
- Authorization 过滤器: 第一个运行的过滤器,决定执行请求的当前用户是否被授权。
- Resource 过滤器: 紧随 Authorization 过滤器之后处理请求的过滤器,其代码在管道的其余部分之前或之后被执行,它们是实现缓存或因性能原因阻塞请求的最佳位置,它们在「Model Binding」之前运行,所以其代码可以影响模型绑定。
- Action 过滤器: 在特定的 Action 之前或之后运行的过滤器,它们被用于操作传入 Action 的参数以及被其返回的结果。
- Exception 过滤器: 用于将全局策略应用于在返回响应之前未被捕获的异常。
- Result 过滤器: 在 Action 返回结果之前或之后执行代码,仅当 Action 方法成功之后才会运行,它们被用于处理必须围绕视图的逻辑。
实现
过滤器通过定义不同的接口支持同步及异步实现。同步实现以 OnStageExecuting 和 OnStageExecuted 方法为模板,例如 OnActionExecuting 在方法执行之前运行,OnActionExecuted 在方法执行之后运行。
1 | using FiltersSample.Helper; |
异步过滤器定义一个 OnStageExecutionAsync 方法,该方法接收一个 FilterTypeExecutionDelegate 委托参数 next,next 参数代表 Action 本身,可在其之前或之后添加扩展逻辑:
1 | using System.Threading.Tasks; |
过滤器的同步和异步接口版本只要实现一个就够了,框架会首先检查过滤器类型是否实现了异步接口,如果是,则执行异步版本。否则,才会执行同步版本。如果同时实现了两个接口,则同步接口会被忽略。
IFilterFactory
IFilterFactory 实现了 IFilterMetaData 接口,所以 IFilterFactory 可以当作 IFilterMetaData 接口的实例在管道的任何地方使用。当框架准备调用 Filter 时,首先尝试将其转换成 IFilterFactory 接口,如果转换成功,接下来调用接口的 CreateInstance 方法以构造 IFilterMetaData 实例,这种设计更加灵活,因为不必在应用程序启动时显式指定具体的 Filter 类型。可以在自定义的 Attribute 上实现 IFilterFactory 接口以另一种方式创建 Filter:
1 | public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory |
框架内置过滤器特性 Filter Attribute
框架包含了可继承或自定义的内置 attribute-based 形式的 Filter。例如以下 ResultFilter 在响应头添加了一个 Header。
1 | using Microsoft.AspNetCore.Mvc.Filters; |
特性允许 Filter 接收参数,在上面的例子中,可以用该 Attribute 修饰一个 Controller 或 Action 方法并指定 Http Header 的名称。
1 | [] |
Index Action 的结果如下:
Filter 特性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
TypeFilterAttribute 和 ServiceFilterAttribute 将在后文介绍。
Filter 应用级别和执行顺序
过滤器可以以三种级别应用 - 以 Attribute 的形式应用在特定的 Action 或 Controller 类上。或者在 ConfigureServices 方法中配置 MvcOptions.Filters 以全局方式应用到所有 Action 和 Controller 上。
1 | public void ConfigureServices(IServiceCollection services) |
多个内置的 Filter 接口都实现了对应的 Attribute 用于继承以支持自定义实现。
过滤器的默认执行顺序
当管道中多处都应用了过滤器,scope 决定了过滤器的默认执行顺序。该序列看来如下:
- 全局过滤器的 before 部分
- Controller 级别过滤器的 before 部分
- Action 方法级别过滤器的 before 部分
- Action 方法级别过滤器的 after 部分
- Controller 级别过滤器的 after 部分
- Controller 级别过滤器的 before 部分
- 全局过滤器的 after 部分
所有继承自
Controller类型的控制器都包含OnActionExecuting和OnActionExecuted方法。这些方法先于任何应用其上的过滤器的OnActionExecuting方法并且后于OnActionExecuted方法。
重写默认顺序
可以通过实现 IOrderedFilter 接口来重写过滤器的默认执行顺序。该接口暴露一个 Order 属性先与过滤器的应用级别来影响执行顺序。Order 的属性值越低,其 before 部分代码先执行,after 部分后执行。
取消和短路
通过在 context 上设置 Result 属性可以在任何点上短路管道。
依赖注入
过滤器以服务的形式注册到 IoC,但实现为 Attribute 的过滤器无法通过依赖注入的方式获取构造器参数,这是因为 Attribute 类型的构造器参数必须在使用时指定,这是 Attribute 的一个限制。
如果过滤器在其创建过程种依赖 DI,可以通过以下方式之一解决该问题:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory
你可能想要在过滤器中从 DI 获取 logger。应该避免将过滤器仅用作日志目的,因为框架内置的日志系统介绍了更科学的记录日志的方法,如果一定要将日志功能加入过滤器逻辑中,那么该日志应该侧重于记录该过滤器与业务领域逻辑相关的内容,而不是
MVC Action或其他框架事件。
ServiceFilterAttribute
ServiceFilterAttribute 从 DI 中请求一个特定过滤器的实例,你应该在 ConfigureServices 方法中注册该过滤器类型,并在 ServiceFilter 引用它。
1 | public void ConfigureServices(IServiceCollection services) |
TypeFilterAttribute
TypeFilterAttribute 与 ServiceFilterAttribute 非常相似,但它并不是从 DI 中解析过滤器类型的实例,而是通过 Microsoft.Extensions.DependencyInjection.ObjectFactory 创建过滤器类型的实例。因此:
- 被
TypeFilterAttribute类型引用的过滤器类型无需在 IoC 中注册。 TypeFilterAttribute可以有选择地传递过滤器类型的构造函数参数。
以下示例演示了如何将构造函数参数传递给 TypeFilterAttribute:
1 | [ |
Authorization 过滤器
- 管道中第一批被执行的过滤器
- 有 before 方法,没有 after 方法
- 不要在该过滤器中的抛出异常
Resource 过滤器
- 实现
IResourceFilter或IAsyncResourceFilter之一 - 包围大多数管理过滤器
- 只有
Authorization过滤器先于它执行
Resource 过滤器用于短路请求的大多数工作,例如,缓存过滤器如果检查到响应位于缓存中,则跳过管道中的其余逻辑
Action 过滤器
实现 IActionFilter 或 IAsyncActionFilter 接口之一,以下是一个示例 Action 过滤器:
1 | public class SampleActionFilter : IActionFilter |
ActionExecutingContext 提供了以下属性:
ActionArguments: 操作提供给方法的输入参数Controller: 操作控制器实例Result: 设置该值以短路该 Action 方法及其后的过滤器,抛出异常亦然,但会被认为是一个失败请求。
ActionExecutedContext 提供了以下属性:
Canceled: 当 Action 执行短路时为 trueException: 抛出异常时该值不为空,将该值显式设置为null则被认为异常已经过处理,之后Result属性将被处理。
对于 IAsyncActionFilter,执行 ActionExecutionDelegate 将:
- 同时执行所有后续的过滤器和 Action 方法本身
- 返回
ActionExecutedContext
要短路,则设置 ActionExecutingContext.Result 并且不要调用 ActionExecutionDelegate。
Exception 过滤器
Exception 实现 IExceptionFilter 或 IAsyncExceptionFilter 接口之一。其用于实现通用错误处理策略。Exception 过滤器:
- 没有 before 和 after 事件
- 实现
OnException或OnExceptionAsync - 处理发生在创建控制器,模型绑定,
Action过滤器或Action方法中未捕获的异常 - 不要在
Resource过滤器,Result过滤器或 MVC Result 执行过程中捕获异常
设置 ExceptionContext.ExceptionHandled 为 true 并返回一个请求响应以指示异常被处理。Exception 过滤器:
- 对于处理 MVC Action 中抛出的异常是最佳位置
- 不如错误处理中间件来得灵活
「仅当需要针对性对某个 MVC Action 进行特殊处理时采用 Exception 过滤器。」
在管道中使用过滤器中间件
Resource 过滤器由于其包围了管道其后的所有执行过程,所以其职责非常类似于一个过滤器中间件。然而其与中间件最大的区别在于,Resource 过滤器是 MVC 的一部分,这意味着它们可以访问 MVC 的 context 和构造。
当使用中间件时同时也希望能够访问 MVC 的路由数据或仅仅为某个特定的 Controller 或 Action 服务。定义一个包含 Configure 方法的类型,以下示例展示了一个使用本地化中间件为请求建立当前文化:
1 | public class LocalizationPipeline |
之后,可以在 Controller 上或 Action 上或全局地通过 MiddlewareFilterAttribute 应用该中间件:
1 | [] |
中间件过滤器与 Resource 过滤器运行在同一阶段。