ASP.NET MVC中的ActionFilter是如何执行的?

   在ASP.NET MVC中的四大筛选器(Filter),ActionFilter直接应用在某个Action方法上,它在目标Action方法执行前后对调用进行拦截以 执行一些额外的操作。这是一种典型的AOP式的设计,如果我们需要在执行某个Action方法的前后执行一些操作,可以通过定义ActionFilter 来实现。本篇文章主要讲述多一个应用到相同Action方法上的ActionFilter的执行机制。
一、ActionFilter
ActionFilter允许我们在目标Action方法执行前后对调用进行拦截以执行一些额外的操作,所有的ActionFilter实现了具有如下定义的接口IActionFilter。

双击代码全选

1

1:public interface IActionFilter

 

双击代码全选

1

2: {

 

双击代码全选

1

3:void OnActionExecuting(ActionExecutingContext filterContext);

 

双击代码全选

1

4:void OnActionExecuted(ActionExecutedContext filterContext);

 

双击代码全选

1

5: }

 

双击代码全选

1

 

 

双击代码全选

1

7:publicclass ActionExecutingContext : ControllerContext

 

双击代码全选

1

8: {

 

双击代码全选

1

9:public ActionExecutingContext();

 

双击代码全选

1

10:public ActionExecutingContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> actionParameters);

 

双击代码全选

1

11:

 

双击代码全选

1

12:publicvirtual ActionDescriptor            ActionDescriptor { get; set; }

 

双击代码全选

1

13:publicvirtual IDictionary<string, object> ActionParameters { get; set; }

 

双击代码全选

1

14:public ActionResult                        Result { get; set; }

 

双击代码全选

1

15: }

 

双击代码全选

1

16:

 

双击代码全选

1

17:publicclass ActionExecutedContext : ControllerContext

 

双击代码全选

1

18: {

 

双击代码全选

1

19:public ActionExecutedContext();

 

双击代码全选

1

20:public ActionExecutedContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, bool canceled, Exception exception);

 

双击代码全选

1

21:

 

双击代码全选

1

22:publicvirtual ActionDescriptor     ActionDescriptor { get; set; }

 

双击代码全选

1

23:publicvirtualbool                 Canceled { get; set; }

 

双击代码全选

1

24:publicvirtual Exception            Exception { get; set; }

 

双击代码全选

1

25:publicbool                         ExceptionHandled { get; set; }

 

双击代码全选

1

26:public ActionResult                 Result { get; set; }

 

双击代码全选

1

27: }

 

如 上面的代码片断所示,IActionFilter接口中定义了两个方法OnActionExecuting和OnActionExecuted,这两个方 法分别在目标Action方法执行前后被调用,它们的参数类型分别为ActionExecutingContext和 ActionExecutedContext。这两个上下文了类型均是ControllerContext的子类。
我们可以从 ActionExecutingContext对象中获取到用于描述当前Action的ActionDescriptor,以及参数列表。 ActionFilter可以在OnActionExecuting方法中对ActionExecutingContext对象的Result属性进行赋 值来直接响应当前的请求。一旦ActionExecutingContext的Result属性被成功赋值,将会终止后续ActionFilter和最终 目标方法的执行。
ActionExecutedContext具有额外的三个属性,Exception表示执行Action方法过程中抛出 的异常,而ExceptionHandled是一个表示是否对异常已经做出处理的标记。Canceled属性表示没有完成整个ActionFilter链 和目标Action方法的执行而中途被终止。

二、ActionFilter的执行机制

当 ActionInvoker在执行目标Action方法之前,会根据Order和Scope属性对用于封装ActionFilter的Filter对象进 行排序。然后根据当前ControllerContext和ActionDescriptro创建一个ActionExecutingContext对 象,并将其作为参数依次调用所有ActionFilter的OnActionExecuting方法。
在这之后真正的目标Action方法 被执行,ActionInvoker随后执行后续的筛选操作。具体来说,它根据当前ControllerContext、 ActionDescriptro以及Action方法执行过程中抛出的异常创建一个ActionExecutedContext对象。该 ActionExecutedContext的Cancel属性为False,如果Action方法返回一个ActionResult对象,该对象将会作 为该ActionExecutedContext的Result属性。
接下来按照相反的次序依次调用ActionFilter对象的 OnActionExecuted方法,执行过程中的ActionFilter可以修改ActionExecutedContext的Result属性。 当整个ActionFilter链执行结束之后,ActionExecutedContext的Result属性返回的ActionResult将会作为 对当前请求的响应。右图基本上反映了连同目标Action在内的整个ActionFilter链的执行过程。

三、ActionFilter对ActionResult的设置

上 面我们已经提到过,在ActionFilter链进行OnActionExecuting方法调用的过程中,一旦某个ActionFilter为 ActionExecutingContext的Result属性设置了一个ActionResult对象,后续ActionFilter和目标 Action将不会被执行。实际上此时ActionInvoker此时会创建一个ActionExecutedContext对象,设置的 ActionResult直接作为其Result属性,而Cancel属性被设置为True。我们现在考虑的问题是:之前的ActionFilter的 OnActionExecuted是否还被执行呢?
为了弄清楚这个问题,我们来创建一个测试程序。在通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中我们定义了如下三个ActionFilter(FooAttribute、BarAttribute和 BazAttribute),它们都继承自我们自定义的FilterBaseAttribute。在FilterBaseAttribute中实现的 OnActionExecuting和OnActionExecuted方法中,我们将ActionFilter自身的类型和执行方法名写入当前 HttpResponse并最终呈现在浏览器中。BarAttribute重写了OnActionExecuting方法,在调用基类同名方法之后为 ActionExecutingContext的Result设置了一个EmptyResult对象。

双击代码全选

1

1:public abstract class FilterBaseAttribute : FilterAttribute, IActionFilter

 

双击代码全选

1

2: {

 

双击代码全选

1

3:publicvirtualvoid OnActionExecuted(ActionExecutedContext filterContext)

 

双击代码全选

1

4:     {

 

双击代码全选

1

5:         filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuted()<br/>", this.GetType().Name));

 

双击代码全选

1

6:     }

 

双击代码全选

1

 

 

双击代码全选

1

8:publicvirtualvoid OnActionExecuting(ActionExecutingContext filterContext)

 

双击代码全选

1

9:     {

 

双击代码全选

1

10:         filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));

 

双击代码全选

1

11:     }

 

双击代码全选

1

12: }

 

双击代码全选

1

13:

 

双击代码全选

1

14:publicclass FooAttribute : FilterBaseAttribute

 

双击代码全选

1

15: {}

 

双击代码全选

1

16:publicclass BarAttribute : FilterBaseAttribute

 

双击代码全选

1

17: {

 

双击代码全选

1

18:public override void OnActionExecuting(ActionExecutingContext filterContext)

 

双击代码全选

1

19:     {

 

双击代码全选

1

20:         base.OnActionExecuting(filterContext);

 

双击代码全选

1

21:         filterContext.Result = new EmptyResult();

 

双击代码全选

1

22:     }

 

双击代码全选

1

23: }

 

双击代码全选

1

24:publicclass BazAttribute : FilterBaseAttribute

 

双击代码全选

1

25: {}