zoukankan      html  css  js  c++  java
  • 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方法执行前后被调用,它们的参数类型分别为ActionExecutingContextActionExecutedContext。这两个上下文了类型均是ControllerContext的子类。

    我们可以从ActionExecutingContext对象中获取到用于描述当前Action的ActionDescriptor,以及参数列表。ActionFilter可以在OnActionExecuting方法中对ActionExecutingContext对象的Result属性进行赋值来直接响应当前的请求。一旦ActionExecutingContext的Result属性被成功赋值,将会终止后续ActionFilter和最终目标方法的执行。

    ActionExecutedContext具有额外的三个属性,Exception表示执行Action方法过程中抛出的异常,而ExceptionHandled是一个表示是否对异常已经做出处理的标记。Canceled属性表示没有完成整个ActionFilter链和目标Action方法的执行而中途被终止。

    二、ActionFilter的执行机制

    image当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: }

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

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

    四、ActionFilter中的异常处理

    image通过上面的介绍我们知道了某个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.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    零基础学习python_异常处理(32-33课)
    零基础学习python_pickle(31课)
    零基础学习python_文件(28-30课)
    安全测试3_Web后端知识学习
    安全测试2_Web前端知识学习
    安全测试1_Web知识简介
    659 为什么说服人那么难?(一个理智的人不要总试图改变别人的世界观,应该找机会颠覆自己的世界观)
    总结18,展望19
    typescript 的安装
    MVC4.0 解决Controllers与Areas中控制器不能同名问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2624621.html
Copyright © 2011-2022 走看看