您的位置:首页 > 编程语言 > ASP

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

2012-08-06 09:31 676 查看
[code] public interface IActionFilter

{

void OnActionExecuting(ActionExecutingContext filterContext);

void OnActionExecuted(ActionExecutedContext filterContext);

}


public class ActionExecutingContext : ControllerContext

{

 public ActionExecutingContext();

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

 

 public virtual ActionDescriptorActionDescriptor{ get; set;}

 public virtual IDictionary<string, object> ActionParameters{ get; set;}

 public ActionResultResult{ get; set;}

}


public class ActionExecutedContext : ControllerContext

{

 public ActionExecutedContext(); 

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

 

 public virtual ActionDescriptor ActionDescriptor{ get; set;}

 public virtual bool Canceled{ get; set;}

 public virtual ExceptionException{ get; set;}

 public bool ExceptionHandled{ get; set;}

 public ActionResult Result{ get; set;}

}

[/code]
[/code]
如上面的代码片断所示,IActionFilter接口中定义了两个方法OnActionExecuting和OnActionExecuted,这两个方法分别在目标Action方法执行前后被调用,它们的参数类型分别为ActionExecutingContextActionExecutedContext。这两个上下文了类型均是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对象。

[code]
[code] public abstract class FilterBaseAttribute : FilterAttribute, IActionFilter

{

 public virtual void OnActionExecuted(ActionExecutedContext filterContext)

{

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

}


 public virtual void OnActionExecuting(ActionExecutingContext filterContext)

{

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

}

}


public class FooAttribute : FilterBaseAttribute

{}

public class BarAttribute : FilterBaseAttribute

{

 public override void OnActionExecuting(ActionExecutingContext filterContext)

{

 base.OnActionExecuting(filterContext);

 filterContext.Result = new EmptyResult();

}

}

public class BazAttribute : FilterBaseAttribute

{}

[/code]
[/code]
然后我们定义了如下一个HomeController,上面定义的三个ActionFilter特性被应用到了Action方法Index上。我们对三个ActionFilter特性的Order属性作了相应地设置使它们可以按照我们希望的顺序(FooAttribute =>BarAttribute =>BazAttribute)执行。

[code]
[code] public class HomeController : Controller

{

 [Foo(Order = 1)]

 [Bar(Order = 2)]

 [Baz(Order = 3)]

 public void Index()

{

 Response.Write("Index...</br>");

}

}

[/code]
[/code]


运行该程序后会在浏览器中呈现出如左图所示的输出结果,从中可以看出对于应用到Action方法Index上的三个ActionFilter,当BarAttribute的OnActionExecuting方法执行并对ActionExecutingContext的Result属性进行了相应设置后,在它之前的ActionFilter的OnActionExecuted方法依然还是会执行。



这个简单的实例演示揭示了应用到同一个Action方法上的ActionFilter链的执行机制:如果某个某个ActionFilter在执行OnActionExecuting方法过程中对ActionExecutingContext的Result属性进行了设置,后续的ActionFilter和目标Action方法将不会再执行。此时ActionExecutedContext对象被创建,通过ActionExecutingContext的Result属性表示的ActionResulut对象将会赋值给ActionExecutedContext的Result属性。然后以前一个ActionFilter作为起点将创建的ActionExecutedContext对象作为输入参数调用它们的OnActionExecuted方法。右图基本上揭示了整个ActionFilter链执行的流程。顺便指出一点:某个ActionFilter在OnActionExecuted方法中对ActionExecutedContext的Result的设置对整个ActionFilter链的执行没有影响。

四、ActionFilter中的异常处理



通过上面的介绍我们知道了某个ActionFilter在执行OnActionExecuting/OnActionExecuted方法过程中设置ActionExecutingContext/ActionExecutedContext的Result属性进行设置后会对整个ActionFilter链的执行造成怎样的影响,接下来我们来讨论一下如果某个ActionFilter在执行OnActionExecuting/OnActionExecuted方法抛出异常,整个ActionFilter链又会如何执行。

如果第一个ActionFilter在执行OnActionExecuting或者OnActionExecuted方法的过程中出现异常,那么这个异常会被直接抛出。对于出现异常的并不是第一个ActionFilter,那么异常会被捕捉并据此创建一个ActionExecutedContext对象(其Canceled属性为False)作为参数调用前一个ActionFilter的OnActionExecuted方法。在前一个ActionFilter的OnActionExecuted方法执行之后ActionExecutedContext的ExceptionHandled属性为True,会按照正常的方式调用之前ActionFilter的OnActionExecuted方法。

反之,如果ExceptionHandled属性为False,则会直接将异常抛出来,而这个被抛出的异常又会被之前的ActionFilter捕捉到,而这个ActionFilter又会根据捕捉的异常创建一个ActionExecutedContext对象并调用自身的OnActionExecuted方法。如果异常是在非链头的ActionFilter的OnActionExecuted方法中抛出的,处理流程与此类似。

我们不妨举例说明Action链在执行过程中对异常的处理。假设具有如左图所示的4个ActionFilter被应用到目标Action方法上,现在Filter1、Filter2和Filter3的OnActionExecuting方法异常被正常调用,但是Filter4在执行OnActionExecuting方法的时候抛出一个异常。该异常会被Filter3捕捉,它会根据这个异常创建一个ActionExecutedContext对象,并作为参数调用自己的OnActionExecuted方法(步骤1)。

如果Filter3在执行OnActionExecuted方法后ActionExecutedContext的ExceptionHandled属性为False,它会直接将异常抛出来。再次抛出的异常又会被Filter2所捕捉,它按照Filter3的方式根据异常创建ActionExecutedContext对象并作为参数调用自己的OnActionExecuted方法(步骤2)。如果Filter2在执行OnActionExecuted方法的时候将ActionExecutedContext对象的ExceptionHandled属性设置为True,那么在这之后会正常地调用Filter1的OnActionExecuted方法,最终不会有异常抛出(步骤3)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: