Asp.net MVC源碼分析–Action Filter的鏈式調用

來源:互聯網
上載者:User

上一篇中我們介紹了asp.net MVC 的Filter的種類,以及調用的時點.今天我們來看一下ActionFilter/ResultFilter 調用的細節以及源碼中令人叫絕的代碼實現.首先我們看到在Contoller這個類中已經實現了IActionFilter/IResultFilter,並且它們的介面實現是調用兩個虛函數來實現的,這就為我們提供了便利,可以在我們的Controller中重寫這些虛函數來截獲並實現我們自己的邏輯.

Controller.cs

View Code

 1   protected virtual void OnActionExecuting(ActionExecutingContext filterContext) {
2 }
3
4 protected virtual void OnActionExecuted(ActionExecutedContext filterContext) {
5 }
6
7 protected virtual void OnAuthorization(AuthorizationContext filterContext) {
8 }
9
10 protected virtual void OnException(ExceptionContext filterContext) {
11 }
12
13 protected virtual void OnResultExecuted(ResultExecutedContext filterContext) {
14 }
15
16 protected virtual void OnResultExecuting(ResultExecutingContext filterContext) {
17 }
18
19
20 #region IActionFilter Members
21 void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext) {
22 OnActionExecuting(filterContext);
23 }
24
25 void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext) {
26 OnActionExecuted(filterContext);
27 }
28 #endregion
29
30 #region IResultFilter Members
31 void IResultFilter.OnResultExecuting(ResultExecutingContext filterContext) {
32 OnResultExecuting(filterContext);
33 }
34
35 void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext) {
36 OnResultExecuted(filterContext);
37 }
38 #endregion

但是我們如何來擷取這個由Controller類實現的Filter介面呢?請看ControllerInstanceFilterProvider,這個已經預設註冊的Filter Provider可以得到這些Filter的介面實現.

ControllerInstanceFilterProvider.cs

View Code

1  public class ControllerInstanceFilterProvider : IFilterProvider {
2 public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
3 if (controllerContext.Controller != null) {
4 // Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
5 yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
6 }
7 }
8 }

---------------------------------------------------------------------------------------------

但是請大家考慮一個問題,如果我們在Action 上也同時標記了Action Filter的時候會產生一次調用需要調用兩個或多個Action filter的時候MVC 怎麼處理這樣的情況呢? 請看在ControllerActionInvoker.InvokeAction方法中的InvokeActionMethodWithFilters 方法.  我們今天的主角登場了,嘿嘿.

ControllerActionInvoker.cs

View Code

 1    protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
2 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
3 Func<ActionExecutedContext> continuation = () =>
4 new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
5 Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
6 };
7
8 // need to reverse the filter list because the continuations are built up backward
9 Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
10 (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
11 return thunk();
12 }

首先我們看到代碼中聲明了Func<ActionExecutedContext> continuation, 的這樣一個委託, 注意這裡只是聲明還沒有調用哦.這個委託是用來調用Action方法的. 接下來又聲明了 Func<ActionExecutedContext> thunk 這樣一個委託, 這個委託的內容是用filters.Aggregate 方法來合并我們的Filter執行鏈,(這裡不理解的話,我們需要看Aggregate 方法的說明). InvokeActionMethodFilter的方法是執行IActionFilter中的介面實現.

InvokeActionMethodFilter方法

View Code

 1  internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) {
2 filter.OnActionExecuting(preContext);
3 if (preContext.Result != null) {
4 return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) {
5 Result = preContext.Result
6 };
7 }
8
9 bool wasError = false;
10 ActionExecutedContext postContext = null;
11 try {
12 postContext = continuation();
13 }
14 catch (ThreadAbortException) {
15 // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
16 // the filters don't see this as an error.
17 postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
18 filter.OnActionExecuted(postContext);
19 throw;
20 }
21 catch (Exception ex) {
22 wasError = true;
23 postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
24 filter.OnActionExecuted(postContext);
25 if (!postContext.ExceptionHandled) {
26 throw;
27 }
28 }
29 if (!wasError) {
30 filter.OnActionExecuted(postContext);
31 }
32 return postContext;
33 }

---------------------------------------------------------------------------------------------
讓我們來嘗試理解上面的代碼,首先請看Aggregate 方法的調用說明以及Demo:

View Code

 1 public static TAccumulate Aggregate<TSource, TAccumulate>(
2 this IEnumerable<TSource> source,
3 TAccumulate seed,
4 Func<TAccumulate, TSource, TAccumulate> func
5 )
6 //demo
7
8 int[] ints = { 4, 8, 8, 3, 9, 0, 7, 8, 2 };
9 // Count the even numbers in the array, using a seed value of 0.
10 int numEven = ints.Aggregate(0, (total, next) =>
11 next % 2 == 0 ? total + 1 : total);
12
13 Console.WriteLine("The number of even integers is: {0}", numEven);
14
15 // This code produces the following output:
16 //
17 // The number of even integers is: 6

我們看到Aggregate 第一個參數是初始值同時也決定了Aggregate 方法的傳回型別, 第二個參數是一個委託,委託的第一個參數是上一次調用的返回,這裡返回的類型也是一個委託.

---------------------------------------------------------------------------------------------

這時我們再看我們的InvokeActionMethodWithFilters 方法中 thunk 變數的聲明:

第一個參數是continuation,它的類型是Func<ActionExecutedContext>,這就決定了我們Aggregate 方法的傳回型別也是Func<ActionExecutedContext>, 再看第二個參數也是一個委託(注意這裡這個方法也沒有調用哦.)它的實現是調用InvokeActionMethodFilter方法. 這個委託會做為第三次迭代(如果有的話)Next參數.

所以最後在InvokeActionMethodFilter 方法的Next參數第一次是continuation變數,第二次是() => InvokeActionMethodFilter(filter, preContext, next));

.如果有第三次或第N次(有三個或N個Filter的情況)會同樣第二次的操作, 最後返回給thunk變數的是最後一次產生的調用委託.

---------------------------------------------------------------------------------------------

哎這裡好饒哦,對不起,我實在找不到其它詞彙了.只能靠最後的一張圖來協助大家理解了.

聲明時的虛擬碼:

var fun1 = (next, filter) => () => InvokeActionMethodFilter(filter, preContext, continuation);
var fun2 = (next, filter) => () => InvokeActionMethodFilter(filter, preContext, fun1)
var fun3 = (next, filter) => () => InvokeActionMethodFilter(filter, preContext, fun2)

最終thunk = fun3;

當thunk()調用的時候它的執行順序是這樣的.

調用時的虛擬碼:

invoke (next, filter) => () => InvokeActionMethodFilter(filter, preContext, fun3)

invoke  (next, filter) => () => InvokeActionMethodFilter(filter, preContext, fun2)

invoke  (next, filter) => () => InvokeActionMethodFilter(filter, preContext, continuation);

 

最後ResultFilter的調用和以上的分析是一樣的,理解了這個就理解的ResultFilter的調用.

 

後記;

相關文章:

1.巧用Aggregate和委託構造遞迴鏈

 

轉載請註明出處:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html 

本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.