位置:海鸟网 > IT > ASP.NET >

ASP.Net MVC不在多个Action上写同样的FitlerAttribute

 以AuthorizeAttribute这个Filter举例,一个Controller有若干个Action,包括登录的Action(如Login)。这时我们就不能将Authorize放在Controller签名之上,不得不给除了Login这个Action之外的所有Action加上个Authorize——这也不是大不了的事情,我多写几个Authorize也觉得没关系,或者我重新实现一个IAuthorizationFilter,在里面判断如果是Login这个Action,就不进行验证。

如果我想给所有Action注入一段html到页面底部,我必须去修改Controller吗?
如果我想动态控制某个Action允许由哪些角色访问,我通过修改Controller能实现吗?
如果我想这时候控制的由哪些角色来访问,需求改变时我想要控制由哪些用户来访问呢?我还得去修改Controller吗?或者增加或修改Filter吗?

所以需求就出现了:能不能让我集中管理这些Filter,并且控制的更灵活呢?能让所有Controller和Action签名上都干干净净的那就最好了。

那就把Filter放在一处集中管理,在Action或ActionResult等执行Filter之前保证将需要的Filter准备好就行了。

在解决问题之前,先简单回顾一下Action执行前后发生的事。

我们知道,在ASP.NET MVC中,每一次请求通常都定位到一个具体的Controller的Action中。
在默认情况下,由Action执行器ControllerActionInvoker类去控制Action的执行(或不执行),实际做事的是InvokeAction方法。
InvokeAction方法首先去查找Action(由FindAction方法),如果Action被找到了,会去检索该Action拥有的Filter以及该Action所属Controller的Filter(由GetFilters方法),将找到的所有Filter放入一个FilterInfo变量中,FilterInfo中保存的Filter不完全是Action是自己的。然后先执行找到的所有IAuthorizationFilter(由InvokeAuthorizationFilters方法)。当然,在InvokeAuthorizationFilters方法中,只要有IAuthorizationFilter的ActionResult不为null就会返回不会执行其他的了。 InvokeAction方法检测执行结果,如果ActionResult的确不为空,则执行该Result,其他Filter就不管啦。 接着获取要传给Action的参数集(交给GetParameterValue方法),就执行InvokeActionMethodWithFilters方法,方法名已经足够说明它是干什么的了。如果一切正常,根据InvokeActionMethodWithFilters方法返回的结果去接着就执行InvokeActionResultWithFilters方法。在执行InvokeAuthorizationFilters一直到执行InvokeActionResultWithFilters的这一整个过程中如果发生异常,则根据捕获的异常执行InvokeExceptionFilters进行异常处理。

这里需要注意一点:InvokeAction、GetFilters、InvokeAuthorizationFilters、GetParameterValue、InvokeActionMethodWithFilters、InvokeActionResultWithFilters、InvokeExceptionFilters等全是虚方法,除非有足够的原因去继承IActionInvoker重写一个Action执行器,否则重写某些方法足够扩展。

甚至ControllerActionInvoker类本身,在ASP.NET MVC基础架构中也是可以替换的,怎么替换呢?继承Controller类时重写CreateActionInvoker方法就可以。

另外还可以在构造Controller对象给它的ActionInvoker属性赋值,这又怎么赋值?重写DefaultControllerFactory创建Controller实例的GetControllerInstance方法。 然后在Applicaion_Start中设置新的ControllerFactor:
ControllerBuilder.Current.SetControllerFactory(new YourControllerFactory());

回到主题。 首先我们将Filter和Action的对应关系(Filter和Controller的对应关系在本文中暂不讨论)存于一个集合中并缓存起来。

从找到Action到执行InvokeAuthorizationFilters之前,必须将IAuthorizationFilter准备好;从找到Action到执行InvokeActionMethodWithFilters之前,必须将Action需要的IActionFilter准备好;从找到Action到执行InvokeActionResultWithFilters之前,必须将ActionResult需要的IResultFilter准备好。异常发生InvokeExceptionFilters执行之前,必须将IExceptionFilter准备好。 基于以上几点,我们好像只能在FindAction方法和GetFilters方法之间选择一个。当然也就是只有GetFilters了。

在Filter方法中,我们根据当前Action的特征(如方法名,或包括请求方式Get或Post)与Filter和Action对应表。将匹配的Filter加进FilterInfo变量中。