无障碍 关怀版
评论

ASP.NET Core MVC 之过滤器(Filter)

ASP.NET MVC 中的过滤器允许在执行管道中的特定阶段之前或之后运行代码。可以对全局,也可以对每个控制器或每个操作配置过滤器。

一、过滤器如何工作

不同的过滤器类型在管道中的不同阶段执行,因此具有各自的与其场景。根据需要执行的任务以及需要执行的请求管道中的位置,选择要创建的过滤器类型。过滤器在 MVC 操作调用管道中运行,有时也称为过滤管道,在 MVC 中选择要执行的操作后,执行操作上的过滤器,如图:

不同的过滤器在管道内的不同位置执行。像授权过滤器这样的过滤器只在管道中靠前的位置执行。其他过滤器,如操作(Action)过滤器,可以在管道执行的其他部分之前和之后执行,如图:

1.选择过滤器

授权过滤器用于确定当前请求用户是否被授权。

资源过滤器是在授权之后第一个处理请求的过滤器,也是最后一个在请求离开过滤管道时接触请求的过滤器。在性能方面,对实现缓存或者对过滤管道进行短路 特别有用。

操作过滤器包装对单个操作方法的调用,并且可以处理传递到操作的参数以及从操作返回的操作结果。

异常过滤器用于对 MVC 应用程序中未处理的异常应用全局策略。

结果过滤器包装单个操作结果的执行,并且尽在操作执行成功时运行。它们必须是围绕视图执行或格式化程序执行的逻辑的理想选择。

2.实现过滤器

所有过滤器均可通过不同的接口定义支持同步和异步的实现。根据需要执行的任务类型,选择同步或异步实现。从框架的角度看,它们是可以互换的。

同步过滤器定义了 OnStageExecuting 和 OnStageExecuted 方法(也有例外)。OnStageExecuting 方法在事件管道阶段之前通过阶段名称来调用,而 OnStageExecuted 方法将在阶段名称命名的管道阶段之后调用。

public class SampleActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { //操作执行前做的事情 } public void OnActionExecuted(ActionExecutedContext context) { //操作执行后做的事情 } }

异步过滤器定义了一个单一的 OnActionExecutionAsync 方法,可以在具体管道阶段的前后运行。OnActionExecutionAsync 方法提供了一个 ActionExecutionDelegate 委托,调用时该委托会执行具体管道阶段的工作,然后等待完成。

    publicclassSampleAsyncActionFilter: IAsyncActionFilter{publicasyncTask OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next) {//操作执行前做的事情awaitnext; //操作执行后做的事情}}
3.过滤器作用域

过滤器有三种不同级别的作用域。你可以在特定的操作上用特性(Attribute)的方式使用特定的过滤器。也可以在控制器上用特性的方式使用过滤器,这样就可以将效果作用在控制器内的所有操作上。或者注册一个全局过滤器,它将作用于整个 MVC 应用程序的每一个操作。

如果想要使用全局过滤器,可以在配置 MVC 时,在 Startup 的 ConfigureServices 方法中添加:

    services.AddMvc( options=> {options.Filters.Add( typeof(SampleActionFilter)); //通过类型options.Filters.Add( newSampleActionFilter); //注册实例}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

过滤器既可以通过类型添加,也可以通过实例添加。如果通过实例添加,则该实例会被使用于每一个请求。如果通过类型添加,则在每次请求后都会创建一个实例,其所有构造函数依赖项都将通过 DI 来填充。

把过滤器接口的实现当作特性使用也非常方便。过滤器特性可应用于控制器和操作方法。框架包含了内置的基于特性的过滤器,可以继承他们或者另外定制。例如,下面的过滤器继承了 ResultFilterAttribute,并重写 OnResultExecuting 方法(在响应中增加一个信息头):

    publicclassAddHeaderAttribute: ResultFilterAttribute{privatereadonlystring_name; privatereadonlystring_value;

    publicAddHeaderAttribute( stringname, stringvalue) {_name = name;_value = value; }publicoverridevoidOnResultExecuting( ResultExecutingContext context) {context.HttpContext.Response.Headers.Add(_name, newstring[] { _value }); base.OnResultExecuting(context); }}

特性允许过滤器接受参数,如下,可将此特性添加到控制器或操作中,并为其指定所需 HTTP 头的名称和值:

    [ AddHeader( "Author", "Ruby Lu") ] publicclassHomeController: Controller{}
以下几种过滤器接口可以自定义为相应特性的实现:

ActionFilterAttribute

ExceptionFilterAttribute

ResultFilterAttribute

FormatFilterAttribute

ServiceFilterAttribute

TypeFilterAttribute

4.取消和短路

通过设置传入过滤器方法的上下文参数中的 Result 属性,可以在过滤器管道的任意一点短路管道。比如,下面的 ShortCircuitingResourceFilter 将阻止它之后管道内的所有过滤器,包括所有操作过滤器:

publicclassShortCircuitingResourceFilter: Attribute, IResourceFilter { publicvoidOnResourceExecuting( ResourceExecutingContext context) { context.Result = newContentResult { Content = "短路" }; } publicvoidOnResourceExecuted( ResourceExecutedContext context) {

}}

二、配置过滤器

全局过滤器在 Startup 中配置。基于特性的过滤器如果不需要任何依赖,可以简单地继承一个已存在地过滤器相对应地特性类型。如果要创建一个非全局作用域,但需要从依赖注入中获得依赖项的过滤器,那么在它们上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,这样就可用于控制器或操作了。

1.依赖注入

以特性形式实现的,直接添加到控制器或操作的过滤器,其构造函数不得由依赖注入提供依赖项。其原因在于,特性所需的构造函数参数必须由使用处直接提供。这是特性原型机理的限制。

如果过滤器需要从 DI 中获得依赖项,那么可以用以下几种方法在类或操作方法使用:

ServiceFilterAttribute

TypeFilterAttribute

IFilterFactory 实现特性

TypeFilter 将为其依赖项从 DI 中使用服务来实例化一个实例。ServiceFilter 则从 DI 中获取一个过滤器实例。下面演示 ServiceFilter:

先在 ConfigureServices 中注册 AddHeaderFilterWithDI 类型:services.AddScoped<AddHeaderFilterWithDI>;

然后使用:

[ ServiceFilter(typeof(AddHeaderFilterWithDI))] publicIActionResult Index( ) {  }

ServiceFilterAttribute 实现了IFilterFactory 接口,它公开了一个创建 IFilter 实例的方法。在 ServiceFilterAttribute 中,IFilterFactory 接口的 CreateInstance 方法被实现为从服务容器加载指定的类型。

TypeFilterAttribute 非常类似 ServiceFilterAttribute (也实现 IFilterFactory 接口),但它的类型不是直接从 DI 容器中解析,相反,它使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 实例化类型。

由于这种差异,使用 TypeFilterAttribute 引用的类型不需要在使用前向容器注册,但它们仍由容器来填充其依赖项。此外,TypeFilterAttribute 可以可选的接受该类型的构造函数参数。下面是 TypeFilterAttribute 演示:

    [ TypeFilter(typeof(AddHeaderAttribute), Arguments = new object[] { "Author", "Ruby"})] publicIActionResult Index( ) {returnView; }

如果有一个简单的过滤器,不需要任何参数,但有构造函数需要通过 DI 填充依赖项,那么可以继承 TypeFilterAttribute,允许使用自己命名的特性类和方法(而不是 [TypeFilterAttribute(typeof(FilterType))])。下面的过滤器显示了如何实现此功能:

    publicclassSampleActionFilterAttribute: TypeFilterAttribute{publicSampleActionFilterAttribute( ) : base( typeof(SampleActionFilterImpl )) {}privateclassSampleActionFilterImpl: IActionFilter{publicvoidOnActionExecuting( ActionExecutingContext context) {//操作执行前做的事情

    }publicvoidOnActionExecuted( ActionExecutedContext context) {//操作执行后做的事情

    }}}

该过滤器可通过使用 [SampleActionFilter] 这样的语法应用于类或方法,而不必使用 [TypeFilter] 或 [ServiceFilter] 。

IFilterFactory 实现 IFilter ,因此在过滤器管道中,任何位置的 IFilterFactory 实例都可当作 Filter 实例来使用。当框架准备调用过滤器时,将尝试将其转换为 IFilterFactory 。如果转换成功, 则调用 CreateInstance 方法来创建将被调用的 IFilter 实例。这是一种非常灵活的设计,因为当应用程序启动时,不需要明确地设置精确地过滤器。

你可以在自己地特性中实现 IFilterFactory 几口,作为另一种创建过滤器的方法:

    publicclassAddHeadWithFactoryAttribute: Attribute, IFilterFactory{publicboolIsReusable { get; } //实现IFilterFactorypublicIFilterMetadata CreateInstance( IServiceProvider serviceProvider) {returnnewInternalAddHeaderFilter; }}

    publicclassInternalAddHeaderFilter: IResultFilter{publicvoidOnResultExecuting( ResultExecutingContext context)\ {context.HttpContext.Response.Headers.Add("Internal", newstring[] { "Header Add"}); }publicvoidOnResultExecuted( ResultExecutedContext context)

    {

    }}

2.排序

过滤器可以应用于操作方法或控制器(通过特性)或添加到全局过滤器集合中。作用域通常也决定了排序,最接近操作的过滤器首先运行。

除了作用域,过滤器还可以通过实现 IOrderedFilter 来重写它们的执行顺序。此接口简单的暴露了一个 int Order 属性,并且过滤器基于该属性以数字升序执行。所有内置的过滤器,包括 TypeFilterAttribute 和 ServiceFilterAttribute ,都实现 IOrderedFilter 接口。,因此当将过滤器特性应用于类或方法时,可以指定过滤器执行顺序。默认情况下,所有内置过滤器的 Order 属性都为0,因此范围用作分隔符,并且是决定性因素(除非 Order 设置为 0)。

每个从 Controller 基类继承的控制器都包含 OnActionExecuting 和 OnActionExecuted 方法。这些方法为给定操作包装了过滤器,它们分别最先运行和最后运行。假设没有为任何过滤器设置 Order 舒总,那么单纯基于范围的顺序为:

控制器的 OnActionExecuting

全局过滤器的OnActionExecuting

类过滤器的OnActionExecuting

方法过滤器的OnActionExecuting

方法过滤器的OnActionExecuted

类过滤器的OnActionExecuted

全局过滤器的OnActionExecuted

控制器过滤器的OnActionExecuted

要修改默认的基于范围的顺序,则应显示设置类级别或者方法级别过滤器的 Order 属性。例如,将 Order = -1 添加到方法级属性:

[MyFilter (Name = "...",Order = -1)]

在这种情况下,小于零的值将确保此过滤器在全局和类级过滤器之前运行:

控制器的 OnActionExecuting

方法过滤器的OnActionExecuting

全局过滤器的OnActionExecuting

类过滤器的OnActionExecuting

类过滤器的OnActionExecuted

全局过滤器的OnActionExecuted

控制器过滤器的OnActionExecuted

方法过滤器的OnActionExecuted

Controller 类的方法总是在所有过滤器之前和之后运行。这些方法不作为IFilter实例实现。也不参与IFilter排序算法。

3.对比中间件

一般来说,过滤器用于处理业务与应用程序的横切关注点,用法和功能很像中间件,但过滤器允许你将作用范围缩小,并将其插入到应用程序中有意义的位置,例如视图之前或模型绑定之后。过滤器是 MVC 的一部分,可以访问其上下文和构造函数。例如,中间件很难检测到请求的模型验证是否产生错误,并且做出相应的响应。返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()
大家都在看
推荐阅读

深圳SEO优化公司肇庆网站优化软件公司大连优化报价光明seo优化价格安康阿里店铺托管济南企业网站改版报价南平网站设计模板哪家好天水网站推广工具大芬网站设计模板哪家好黔东南网站推广系统多少钱桐城seo优化报价黔南网站改版哪家好昭通网络广告推广推荐郴州推广网站多少钱铁岭营销型网站建设推荐晋城SEO按天扣费推荐和田SEO按天收费多少钱固原网站设计公司宜宾seo网站推广推荐宝鸡网络营销报价盐城seo报价朝阳英文网站建设多少钱民治百度竞价包年推广价格四平网站关键词优化价格临沂seo哪家好马鞍山百度关键词包年推广价格坪山模板网站建设价格佛山百姓网标王推广哪家好百色建站报价黔东南企业网站制作推荐桐城建站公司歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化