好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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

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

目录 
一、ActionFilter 
二、ActionFilter的执行机制 
三、ActionFilter对ActionResult的设置 
四、ActionFilter中的异常处理

一、ActionFilter

ActionFilter允许我们在目标Action方法执行前后对调用进行拦截以执行一些额外的操作,所有的ActionFilter实现了具有如下定义的接口 IActionFilter 。

    1:   public  interface IActionFilter
    2:  {    
    3:       void  OnActionExecuting(ActionExecutingContext filterContext);
    4:       void  OnActionExecuted(ActionExecutedContext filterContext);
    5:  }
    6:   
    7:   public   class  ActionExecutingContext : ControllerContext
    8:  {    
    9:       public  ActionExecutingContext();
   10:       public  ActionExecutingContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> actionParameters);
   11:      
   12:       public   virtual  ActionDescriptor            ActionDescriptor { get; set; }
   13:       public   virtual  IDictionary<string, object> ActionParameters { get; set; }
   14:       public  ActionResult                        Result { get; set; }
   15:  }
   16:   
   17:   public   class  ActionExecutedContext : ControllerContext
   18:  {    
   19:       public  ActionExecutedContext();   
   20:       public  ActionExecutedContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor,  bool  canceled, Exception exception);
   21:      
   22:       public   virtual  ActionDescriptor     ActionDescriptor { get; set; }
   23:       public   virtual   bool                  Canceled { get; set; }
   24:       public   virtual  Exception            Exception { get; set; }
   25:       public   bool                          ExceptionHandled { get; set; }
   26:       public  ActionResult                 Result { get; set; }
   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:   public  abstract  class  FilterBaseAttribute : FilterAttribute, IActionFilter
    2:  {
    3:       public   virtual   void  OnActionExecuted(ActionExecutedContext filterContext)
    4:      {
    5:          filterContext.HttpContext.Response.Write(string.Format( "{0}.OnActionExecuted()<br/>" ,  this .GetType().Name));
    6:      }
    7:   
    8:       public   virtual   void  OnActionExecuting(ActionExecutingContext filterContext)
    9:      {
   10:          filterContext.HttpContext.Response.Write(string.Format( "{0}.OnActionExecuting()<br/>" ,  this .GetType().Name));
   11:      }
   12:  }
   13:   
   14:   public   class  FooAttribute : FilterBaseAttribute
   15:  {}
   16:   public   class  BarAttribute : FilterBaseAttribute
   17:  {
   18:       public  override  void  OnActionExecuting(ActionExecutingContext filterContext)
   19:      {
   20:          base.OnActionExecuting(filterContext);
   21:          filterContext.Result =  new  EmptyResult();
   22:      }
   23:  }
   24:   public   class  BazAttribute : FilterBaseAttribute
   25:  {}

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

    1:   public   class  HomeController : Controller
    2:  {
    3:      [Foo(Order = 1)]
    4:      [Bar(Order = 2)]
    5:      [Baz(Order = 3)]
    6:       public   void  Index()
    7:      {
    8:          Response.Write( "Index...</br>" );
    9:      }
   10:  }

运行该程序后会在浏览器中呈现出如左图所示的输出结果,从中可以看出对于应用到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)。

作者: Artech
出处: http://artech.cnblogs测试数据/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  [01] 技术剖析

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ASP.NET MVC中的ActionFilter是如何执行的?的详细内容...

  阅读:41次