Mvc的过滤器是特性类,可以使我们在执行Action之前,执行Action之后,执行Action发生异常时,编写相关的处理代码实现某些逻辑。下面是四个基本的Filter接口。
上面这四个基本的Filter接口又被其他类所继承实现。框架图如下:
可以发现具体的Filter类,如ActionFilterAttribute或者AuthorizeAttribute除了各自实现基本接口IActionFilter和IAuthorizationFilter之外还继承抽象类FilterAttribute,而后者又继承自Attribute类。FilterAttribute除了一个protected的构造函数之外还有一个int类型的Order属性,当在一Controller或者Action上面添加多个相同类型的Filter时定义Filter触发的顺序。ActionFilterAttribute同时继承实现了IActionFilter和IResultFilter接口,这两个接口的函数都实现为abstract函数。如果继承ActionFilterAttribute意味着可以在Action执行之前,之后,Action结果执行之前,之后编写相应的处理代码。
当在一个Action上面定义多个Filter时,如下面的代码。也可以以使用Order属性来控制执行顺序。在编码的过程中发现一个比较蛋疼的问题,就是特性的AllowMultiple会影响到执行顺序和异常抛出时的处理。但是在Mvc的InvokeActionMethodWithFilters源码里面并没有发现对于AllowMultiple的特殊处理呀?奇怪!
[FirstFilter] [SecondFilter] public ActionResult Index(string id, string other) { ViewData["Message"] = "欢迎使用 ASP.NET MVC!"; ViewData["id"] = id; ViewData["other"] = other; return View(); } /* First OnActionExecuting Second OnActionExecuting Second OnActionExecuted First OnActionExecuted First OnResultExecuting Second OnResultExecuting Second OnResultExecuted First OnResultExecuted */ /**************** 当吧FirstFilter定义为AllowMultiple = true时的执行顺序是这样的 Second OnActionExecuting First OnActionExecuting First OnActionExecuted Second OnActionExecuted Second OnResultExecuting First OnResultExecuting First OnResultExecuted Second OnResultExecuted ****************/ /* 当在Second OnActionExecuting中抛出异常时, * 将会跳到First OnActionExecuted ,Second OnActionExecuted不会执行 * 但是如果FirstFilterAttribute的AllowMultiple = true,异常将不会被拦截,直接抛出 First OnActionExecuting Second OnActionExecuting First OnActionExecuted First OnResultExecuting Second OnResultExecuting Second OnResultExecuted First OnResultExecuted */
Controller下的Filter
Controller类除了继承自ControllerBase类之外,还继承了四个基本的Filter接口,这也就意味着同样可以在Controller类里面来定义实现相关方法来实现过滤器的功能,这里有一点需要注意就是Controller里面定义的Filter方法优先级最高,并且不受附加在Controller或者Action之上的Filter的Order影响。These controller filter methods give you a very quick and easy way to add controller code that runs before or after all action methods on that particular controller, or whenever an unhandled exception occurs in that particular controller. 什么时候使用Controller继承自接口的Filter,什么时候自定义一个Filter然后附加在方法之上,这要看情况,当过滤器的方法只是针对某个特殊的Controller时,就使用前者,当过滤器需要被多个Controller公用时,就使用后者。
Authorization Filters
authorization filters 是一个特殊的过滤器,他会在所有其他类型的过滤器触发前被触发。其重要属性如下:
public class MicrosoftController : Controller { [Authorize(Users="billg, steveb, rayo", Roles="chairman, ceo")] public ActionResult BuySmallCompany(string companyName, double price { // Cher-ching! } }
通过上面的定义将使BuySmallCompany只能被用户名为"billg,steven,rayo"之一,角色为"chairman,ceo"之一的访问者访问到(角色和用户名需为"与关系",就是两个必须都符合,不能是"或关系",即只符合其中一项)。如果用户名或者角色不符合将会被设置为Http状态为401,也就是验证失败的状态,这个401状态将会激活项目中启用的验证系统(如Form验证)自动跳转到登陆页面或者显示一个认证失败的页面。Authorization Filters是根据HttpContext.User来判断用户名和角色的。因此可以同Form验证结合。具体怎么结合还需要另外研究。
在使用Mvc Authorization Filters的时候还需要注意一点。就是当项目中结合使用Output Caching时。在缓存项没有被改变的情况下,action的调用的结果是放在缓存中的,在第一次被被访问时直接将这个结果输出给客户端浏览器,不用在触发调用action,因此action的filter也就不会被调用了。这也就意味着会有这么一种情况,经过授权的用户访问某个action,在这个用户访问之后action的结果被缓存下来,接下来未经授权的用户也可以访问这个action。漏洞就来了!微软Mvc团队有意识到这一点,在缓存输出前还是会调用Authorization Filters再验证一次。这也就意味当在自定义定义Authorization Filters时,最好时继承自Authorization Filters,而不是继承自FilterAttribute和 IAuthorizationFilter。以防止在使用缓存时,出现了安全漏洞。