asp.net core 的filter是系统中经常用到的,本文详细分享一下各种filter定义、执行的内部机制以及执行顺序。(asp.net core 系列目录)
一、 概述
asp.net core mvc 中有好几种常用的筛选器,例如authorization filters 、resource filters、action filters 、exception filters 、result filters,他们运行在请求处理管道中的特定阶段,例如authorization filters当前请求的用户是否已授权。 如果请求未获授权,则中止执行后面的请求处理。其他几种filters也类似,只是执行阶段不同。如下图:
图一
filter从定义到执行,本文通过四个阶段说明,如下图:
图二
1.定义:以为例,可以通过继承actionfilterattribute并override它的onactionexecuting和onactionexecuted方法实现。
2.注册:主要有三种方式:在startup的addmvc、controller、action中注册。
3.获取:上一章有介绍,在确定了处理请求的endpoint后,下一步就是创建创建invoker,它有个关键的属性就是filters,它由filterfactory的getallfilters方法获取到。
4.执行:invoker的执行阶段,会进入invokefilterpipelineasync,在这里,各种filter按照图一的方式逐一被执行。
二、filter的定义
filter有好几种,但由于本文主要是分享filter的运行机制,所以只以actionfilter一种来举例,现在定义一个test1filter如下:
public class test1filter : actionfilterattribute { public override void onactionexecuting(actionexecutingcontext context) { base.onactionexecuting(context); //do..... } public override void onactionexecuted(actionexecutedcontext context) { base.onactionexecuted(context); //do...... } }
很简单,可以很方便的通过继承系统提供的actionfilterattribute并override 它的相应方法即可。
三、filter的注册
filter定义好之后就是将其插入到处理管道中,可以在startup的addmvc、controller、action中注册。
1.全局:在startup的addmvc中注册
services.addmvc( options => { options.filters.add(new test6filter()); options.filters.add(new test4filter()); } ).setcompatibilityversion(compatibilityversion.version_2_2);
2.只对指定的controller生效
[test5filter] [test3filter] public class flylolocontroller : controller
3.只对指定的action生效
[test2filter] [test1filter] public jsonresult index()
在实际业务中,我们可以根据具体的需求来确定filter的作用范围。
四、filter的获取
filter的获取是在filterfactory的getallfilters方法中,
public static filterfactoryresult getallfilters(ifilterprovider[] filterproviders, actioncontext actioncontext) { //省略…… var orderedfilters = actiondescriptor.filterdescriptors.orderby(filter => filter,filterdescriptorordercomparer.comparer).tolist(); //省略.... return new filterfactoryresult(staticfilteritems, filters); }
保留了关键的一句话,那就是根据actiondescriptor来获取到它对应的所有filter(无论是针对全局、controller还是action),然后对这些filter进行排序,这里用到了排序方法filterdescriptorordercomparer,它用来定义filter的执行顺序,详细内容见后文。
五、filter的执行
filter的执行在invoker的执行阶段,会进入invokefilterpipelineasync,在这里,各种filter按照图一的方式逐一被执行。具体内容上一章已经进行了详细的描述。它是通过两个while循环实现了如图一的顺序逐一执行。
while (!iscompleted) { await next(ref next, ref scope, ref state, ref iscompleted); }
具体不再赘述。
六、filter的执行顺序
filter的执行顺序由三部分决定:
1.对于不同种的filter,按照图一的顺序执行,例如authorization filters会最先被执行。
2.对于同种的filter,执行顺序由其order和scope来决定。
在filter的获取一节提到了filter的排序方法filterdescriptorordercomparer,它拥有对filter定的排序。
public class filterdescriptorordercomparer : icomparer<filterdescriptor> { public static filterdescriptorordercomparer comparer { get; } = new filterdescriptorordercomparer(); public int compare(filterdescriptor x, filterdescriptor y) { if (x == null) { throw new argumentnullexception(nameof(x)); } if (y == null) { throw new argumentnullexception(nameof(y)); } if (x.order == y.order) { return x.scope.compareto(y.scope); } else { return x.order.compareto(y.order); } } }
从这个方法可以看到filter的执行顺序,按照先order后scope的方式排序。对于继承默认的内置filter的,order默认为0,所有对于这样的filter来说觉得他们顺序的是scope,也就是作用域,默认情况下,全局的为10、controller上的为20、action上的为30.也就是说,filter的执行顺序为
全局 -> controller -> action, 实际的执行顺序是这样的:
全局 onactionexecuting
controller onactionexecuting
action onactionexecuting
action onactionexecuted
controller onactionexecuted
全局 onactionexecuted
也是嵌套的,和中间件的处理方式类似。
当然我们可以自定义filter的order使其不再采用默认值0,只需在其构造函数中设置即可
public class test1filter : actionfilterattribute { public test1filter() { order = 1; } //........... }
3.对于同样作用域的同种filter来说,它们的执行顺序是按照注册先后排列的。
例如:
[test2filter] [test1filter] public jsonresult index()
则先执行test2filter、后执行test1filter。