asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析

  

     这篇文章我们开始看一下ActionFilter,从名字上其实就大概知道ActionFilter就是Action上的Filter,对吧,那么Action上的Filter大概有几个呢???

这个问题其实还是蛮简单的,因为我们听说Mvc本身就是一个扩展性极强的框架,自然就是层层有拦截,层层有过滤,对吧,比如我们看到的如下Controller类。

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
    {
    }

从这个父类的Controller中,我们就可以看到有5个Filter,如:IActionFilter,IAuthenticationFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,

对吧,首先我们还是从第一个ActionFilter说起。

 

一:IActionFilter解析

  现在我们知道IActionFilter是一个接口,接下来感兴趣的就是这个ActionFilter里面到底是什么,比如我下面截图的这样。

    //
    // 摘要:
    //     Defines the methods that are used in an action filter.
    public interface IActionFilter
    {
        //
        // 摘要:
        //     Called after the action method executes.
        //
        // 参数:
        //   filterContext:
        //     The filter context.
        void OnActionExecuted(ActionExecutedContext filterContext);
        //
        // 摘要:
        //     Called before an action method executes.
        //
        // 参数:
        //   filterContext:
        //     The filter context.
        void OnActionExecuting(ActionExecutingContext filterContext);
    }

 从上面这段代码中,我们可以看到其实这个接口里面只有两个方法,一个是OnActionExecuting,一个是OnActionExecuted,看这名字你应该就明白

其实就是在Action的前后分别执行,对吧,那这样的话,聪明的你就想到了应用场景,记录日志,获取action的访问量,以方便后续收费~~~ 接下来我

们来看看怎么来实现这两个方法。

 

1.  用override的方式实现ActionFilter

     现在大家都知道Controller类已经实现了这个接口,那我们自己的XXXController刚好又继承了这个父Controller,所以面对这种情况,我们可以用

override来实现,比如下面我实现的这样。

 1     public class HomeController : Controller
 2     {
 3         public ActionResult Index()
 4         {
 5             return View();
 6         }
 7 
 8         protected override void OnActionExecuting(ActionExecutingContext filterContext)
 9         {
10             filterContext.HttpContext.Response.Write("hello");
11 
12             base.OnActionExecuting(filterContext);
13         }
14 
15         protected override void OnActionExecuted(ActionExecutedContext filterContext)
16         {
17             filterContext.HttpContext.Response.Write("world");
18 
19             base.OnActionExecuted(filterContext);
20         }
21     }

就这样我们就轻松加愉快的实现了,是不是很简单,但是仔细一想,这样的方法还是有一点限制的,也就是说我们override的依赖性太强了,比如说只有

class extends IActionFilter才可以,接下来我们再看有没有更灵活的方法来实现。

 

2.  自定义class extends IActionFilter

    要想做到高度的灵活性,我们必须将这个实现类做成一个“原子单位”,有了这个原子单位,我们就可以很方便的将这个不可拆解的原子性应用到各个地方

去,对吧,这个原子在C#中可以用Attribute来实现,比如下面这样:

 1     public class MyActionFilterAttribute : Attribute, IActionFilter
 2     {
 3         public void OnActionExecuted(ActionExecutedContext filterContext)
 4         {
 5             filterContext.HttpContext.Response.Write("hello");
 6         }
 7 
 8         public void OnActionExecuting(ActionExecutingContext filterContext)
 9         {
10             filterContext.HttpContext.Response.Write("world");
11         }
12     }

 

ok,现在我们已经得到了一个原子性质的MyActionFilterAttribute特性,接下来我们可以将这个MyActionFilterAttribute应用到任何地方,如下图:

1     public class HomeController : Controller
2     {
3         [MyActionFilter]
4         public ActionResult Index()
5         {
6             return View();
7         }
8     }

 

3.  ActionFilterAttribute

     除了我们实现以下Attribute特性和IActionFilter接口,我们还可以继承一个mvc框架提供给我们的ActionFilterAttribute特性,迫不及待的看一下吧~

 1     //
 2     // 摘要:
 3     //     Represents the base class for filter attributes.
 4     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
 5     public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
 6     {
 7         //
 8         // 摘要:
 9         //     Initializes a new instance of the System.Web.Mvc.ActionFilterAttribute class.
10         protected ActionFilterAttribute();
11 
12         //
13         // 摘要:
14         //     Called by the ASP.NET MVC framework after the action method executes.
15         //
16         // 参数:
17         //   filterContext:
18         //     The filter context.
19         public virtual void OnActionExecuted(ActionExecutedContext filterContext);
20         //
21         // 摘要:
22         //     Called by the ASP.NET MVC framework before the action method executes.
23         //
24         // 参数:
25         //   filterContext:
26         //     The filter context.
27         public virtual void OnActionExecuting(ActionExecutingContext filterContext);
28         //
29         // 摘要:
30         //     Called by the ASP.NET MVC framework after the action result executes.
31         //
32         // 参数:
33         //   filterContext:
34         //     The filter context.
35         public virtual void OnResultExecuted(ResultExecutedContext filterContext);
36         //
37         // 摘要:
38         //     Called by the ASP.NET MVC framework before the action result executes.
39         //
40         // 参数:
41         //   filterContext:
42         //     The filter context.
43         public virtual void OnResultExecuting(ResultExecutingContext filterContext);
44     }

 

从这个Attribute中可以看到,它整合了IActionFilter, IResultFilter,自然就有了这两个接口的方法,好了,不多说,我们来实现一下这个抽象类吧。

namespace WebApplication2.Controllers
{
    public class HomeController : Controller
    {
       [MyActionFilter]
       public ActionResult Index()
        {
            return View();
        }
    }

    public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
        }
    }
}

 

二:源码分析

     最好的源码分析方法,肯定是希望你下载一个reflector插件,这样我们就可以获取到运行时的可调试代码以及可以看到的调用堆栈,尤其是”调用堆

栈“,对你来说非常的重要。

 

1. 首先我们下一个断点在 OnActionExecuting方法里面,如下图:

 

2. 通过调用堆栈回退到上一个堆栈,如图:

 

这个方法其实非常的有意思,从方法名称中可以看到,其实它是一个递归的模式,也就是”OnActionExecuting" =>"进栈执行BeginInvokeActionMethod”

=>"退栈执行OnActionExecuted“方法,因为有一个非常好看的statement,比如: 

Func<ActionExecutedContext> continuation = this.InvokeActionMethodFilterAsynchronouslyRecursive(num);

 

好了,更多的细节等待你去考究,希望本篇文章对您有帮助~~~

 

posted @ 2016-07-14 17:25  一线码农  阅读(3937)  评论(2编辑  收藏  举报