[转载]MVC中的扩展点(四)过滤器 – xfrog – 博客园.
过滤器是一组.NET特性,MVC在特定运行时点调用这些特性上的指定方法,以此实现功能注入。MVC包含四个基本的过滤器类型:授权 (Authorization)、活动(Action)、结果(Result)以及异常(Exception)。MVC为这四中过滤器提供了接口定 义:IAuzhorizationFilter、IActionFilter、IResultFilter、IExceptionFilter,所以 MVC在运行时知道如何调用过滤器上的方法。
MVC实现的默认过滤器如以下类图所示:
注意上图中Controller实现了四个基本的过滤器接口,所以在我们的控制器中,可直接重写过滤器方法来实现过滤器功能,此种方式适用于过滤器功能特定于控制器的情景,如果过滤器功能会跨多个控制器使用,那么使用特性的方式可避免重复代码。
各种默认过滤器,都直接或间接的继承于FilterAttribute类,FilterAttribute有一个Order属性,用于过滤器的排序。另 外,MVC为Action过滤器和Result过滤器提供了一个抽象基类ActionFilterAttribute,它同时实现了 IActionFilter和IResultFilter接口。
过滤器的执行过程由控制器的ActionInvoker对象实现,他是一个实现了IActionInvoker接口的类,ActionInvoker是控 制器的一个公共属性,所以我们可以实现自己的IActionInvoker类,然后通过设定Controller的ActionInvoker属性来指定 自定义的Invoker。MVC中默认的ActionInvoker类为ControllerActionInvoker,其执行过滤器列表的过程如下图 所示:
上图表示的是过滤器在没有发生任何异常时的执行顺序:获取过滤器列表——>依次调用按Order排序的授权过滤器,如果某个授权过滤器设置了 AuthorizationContext参数的Result属性,则立即终止剩余授权过滤器的调用,直接执行Result,生成页面应答内容。如果所有 授权过滤器都通过,并且Result为空,则继续调用Action过滤器上的OnActionExecuting方法,如果全部通过,将执行控制器上相应 的Action方法获取ActionResult对象,随后再按相反顺序执行Action过滤器上的OnActionExecuted方法,之后转入到 Result过滤器列表中,同Action类似,先按Order顺序执行OnResutExecuting方法,注意在 OnResultExecuting方法上,过滤器可通过将ResultExecutingContext.Cancel属性设置为True来立即终止 Invoker的执行(即不会执行ActionResult及Result过滤器上的OnResultExecuted方法),如果所有的 OnResultExecuting方法都通过,则通过ActionResult.ExecuteResult方法生成应答内容,最后再按相反顺序调用 Result过滤器上的OnResultExecuted方法。
对于ControllerActionInvoker类在执行过程中产生的异常,会传给Exception过滤器(MVC默认的异常处理过滤器为 HandleErrorAttribute)来处理。在ControllerActionInvoker的各个节点产生的异常会影响到过滤器的执行过程, 下面分几种情况来详细了解:
我们假设某个Action上有A、B两个授权过滤器,有A、B、C三个Action过滤器和Result过滤器以及A、B两个异常过滤器,首先,在没有异常发生时得执行过程如下图:A、B两个异常过滤器未执行,其余所有过滤器都执行
如果在B.OnAuthorization上发生异常,会直接转到异常过滤器上,其余Action过滤器、Result过滤器及Action方法都不会调用:
如果在B.OnActionExecuting上发生异常,则会跳转到Action过滤器列表的上一个过滤器(即A),执行A.OnActionExecuted,然后转到异常过滤器:
如果在B.OnActionExecuting上发生异常,但是在A.OnActionExecuted中将ActionExecutedContext.ExceptionHandled设置为true,此时将会继续执行Result过滤器,并忽略异常过滤器:
如果在执行控制器的Action方法时发生异常,将会继续执行ActionExecuted方法,随后转到异常过滤器:
如果在Action的OnActionExecuted方法上发生异常,将会继续执行完Action过滤器,然后转到异常过滤器:
在Result过滤器上的OnResultExecuting方法和OnResultExecuted方法上发生异常,其处理方式与Action过滤器类似,此处不再详细说明。
如果在Result过滤器OnResultExecuting中将ResultExecutingContext的Cancel属性设置为true,将立即完成Action方法执行:
另外需要注意,在异常过滤器中将ExceptionContext.ExceptionHandled设置为true并不会终止异常过滤器的执行,该属性 仅仅用于标记“异常已被处理”。所以我们可以通过异常过滤器来实现错误日志记录功能,无需担心漏掉”已被处理”的异常。
如果我们要实现自定义的权限验证功能,应该从AuthorizeAttribute类继承,原因在于AuthorizeAttribute已经优化了与OutputCacheAttribute缓存过滤器的协调,避免因缓存原因造成权限验证失效的情况。