asp.net mvc 之旅 —— 第六站 ActionFilter的應用及原始碼分析

一線碼農發表於2016-07-14

  

     這篇文章我們開始看一下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);

 

好了,更多的細節等待你去考究,希望本篇文章對您有幫助~~~

 

相關文章