asp.net mvc源碼分析-Action篇 Filter

來源:互聯網
上載者:User

緊接著上篇 asp.net mvc源碼分析-Controllerl篇 ControllerDescriptor 現在我們該看  FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);這句代碼了,意思很好明白就是擷取當前的FilterInfo資訊,而該方法非常簡單就一句return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));

首先我們來看看_getFiltersThunk是個上面東西:

 private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = (cc, ad) => FilterProviders.Providers.GetFilters(cc, ad);

意思是根據當前的ControllerContext和ActionDescriptor來擷取所有的Filter執行個體,這裡提到一個 FilterProviders.Providers東東,

 public static class FilterProviders {
        static FilterProviders() {
            Providers = new FilterProviderCollection();
            Providers.Add(GlobalFilters.Filters);
            Providers.Add(new FilterAttributeFilterProvider());
            Providers.Add(new ControllerInstanceFilterProvider());
        }

        public static FilterProviderCollection Providers {
            get;
            private set;
        }
    }

看看mvc預設就提供了這3個FilterProvider,GlobalFilters.Filters沒什麼說,意思就是註冊全域的Filiter處理,在我們預設的Global.asax.cs的Application_Start()中有這個一句

 RegisterGlobalFilters(GlobalFilters.Filters);所以GlobalFilters.Filters很好明白,接下來我們看看  Providers.Add(new ControllerInstanceFilterProvider())

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

這個ControllerInstanceFilterProvider 非常特殊,我們可以不用管,之所以會有這個東東,是因為Controller實現了 IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,如果沒有這個ControllerInstanceFilterProvider 那麼Controller實現這些介面就沒有意義了,因為相應的方法沒法調用。

而FilterAttributeFilterProvider的實現相對比較複雜一點,但是很好理解,主要擷取當前Controller和Action的FilterAttribute執行個體,還記得上篇而文章提到的ReflectedAttributeCache裡面有ConcurrentDictionary<MethodInfo, ReadOnlyCollection<FilterAttribute>> _methodFilterAttributeCache和ConcurrentDictionary<Type, ReadOnlyCollection<FilterAttribute>> _typeFilterAttributeCache這兩個東西嗎?

現在我們來看看Filter的建構函式,裡面有這麼幾句需要我們注意

  if (order == null) {
                IMvcFilter mvcFilter = instance as IMvcFilter;
                if (mvcFilter != null) {
                    order = mvcFilter.Order;
                }
            }
            Order = order ?? DefaultOrder;

        Scope = scope;

當我們的Filter是通過ControllerInstanceFilterProvider來建立的話它的order不為null,所以我們不用考慮,而Scope =Global

如果Filter是用過GlobalFilters.Filters來建立的話,那就看在添加是是否有order,沒有order的話,就看當前執行個體是否實現了IMvcFilter,實現了的話就是介面的order,沒有則是-1;而Scope =First

如果是通過FilterAttributeFilterProvider來建立的話,它進來的order是null,但是FilterAttribute是實現了IMvcFilter介面的,所以它的order是預設是Filter.DefaultOrder (-1),而scope是Controller和Action,主要看屬性是在Controller上還是在Action上。

在這些Filter集合中,是有一定的順序的,先按照order從小到大排序,如果order相同就按Scope從小到大排序,最後去掉重複的。

現在我們來看看FilterInfo的夠著函數了:

 public FilterInfo(IEnumerable<Filter> filters) {
            // evaluate the 'filters' enumerable only once since the operation can be quite expensive
            var filterInstances = filters.Select(f => f.Instance).ToList();

            _actionFilters.AddRange(filterInstances.OfType<IActionFilter>());
            _authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());
            _exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());
            _resultFilters.AddRange(filterInstances.OfType<IResultFilter>());
        }

根據當前的filiter集合,把它們分別放到不同filter介面集合中。

下面  AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext,filterInfo.AuthorizationFilters, actionDescriptor);
                    if (authContext.Result != null) {
                        // the auth filter signaled that we should let it short-circuit the request
                        InvokeActionResult(controllerContext, authContext.Result);
                    }

這幾句就很好明白了,對此次請求做身分識別驗證,如果沒有通過則返回一個認證失敗。這個東西很重要尤其是在許可權設計的時候。

驗證 成功後繼續執行

  if (controllerContext.Controller.ValidateRequest) {
                            ValidateRequest(controllerContext);
                        }

Controller.ValidateRequest預設值為true,即是需要驗證用戶端請求的,

  if (controllerContext.IsChildAction) {return;}
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
controllerContext.HttpContext.Request.ValidateInput();

具體的驗證內容很多,很麻煩,這裡就忽略它吧。

說了這麼多我們來總結一下吧:如果我們要給整個應用程式添加一些處理,可以通過GlobalFilters.Filters來添加,如果需要給某個Controller類或則是Action添加特殊處理,可以通過FilterAttributeFilterProvider來添加。在我們實際開發中往往用到的是ActionFilterAttribute

   public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter

當然如果有特殊的需求我們可以添加自己的FilterProvider,如在Application_Start()中可以寫成 FilterProviders.Providers.Add(xxxxx);

接下來我我們看看這些Filter事這麼調用的,憑我們的直覺調用順序應該是

OnActionExecuting
Action ......真正調用Action
OnActionExecuted

OnResultExecuting
Result....真正調用Result
OnResultExecuted

而剩下的代碼就只有這 

 IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
  ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
     InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);幾句了,

 IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);這個方法是用來擷取Action參數對應的值,實現很複雜我們計劃放到後面在將。

看到InvokeActionMethodWithFilters這個方法我們就知道實現的功能是調用Action和IActionFilter的兩個方法:

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

  這段代碼我看了很久才看懂它的細節。 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);這句就沒什麼說的了,簡單一實例化一個ActionExecutingContext對象,

 

  Func<ActionExecutedContext> continuation = () =>
                new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
                    Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
                };

這句有個方法continuation 沒有輸入參數返回ActionExecutedContext執行個體,只是這個執行個體有一個Result 屬性,它的之就是我們調用Action後的傳回值(ActionResult類型)。

  Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
                (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
            return thunk();

這裡關鍵是Aggregate的用法,continuation是一個初始值,將每次 InvokeActionMethodFilter(filter, preContext, next));的結果賦給continuation值,InvokeActionResultFilter的代碼大致如下:

   internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
            filter.OnResultExecuting(preContext);
            bool wasError = false;
            ResultExecutedContext postContext = null;
            postContext = continuation();
            filter.OnResultExecuted(postContext);
            return postContext;
        }

這裡個方法沒有修改continuation的值,當filter迴圈執行  filter.OnResultExecuting(preContext)完了的時候就開始調用continuation方法了,在執行了Action,再次迴圈filter 以執行 filter.OnResultExecuted(postContext);

總之這段代碼寫的很難看懂,我個人認為拆開寫要好些

foreach(Filiter ,...)
{
  filter.OnResultExecuting(preContext);
}
 postContext = continuation();
foreach(Filiter ,...)
{
   filter.OnResultExecuted(postContext);
}

我當時為了理解這段代碼自己寫了一個文法測試

  class Program      {          static void Main(string[] args)          {              List<string> users = new List<string> { "majiang", "luyang", "xieyichuan" };              Func<string> seed = () => "First";              Func<Func<string>, string, Func<string>> next = (fun, str) => delegate()              {                  Console.WriteLine(str);                  var temp = fun();                  temp += "-" + str;                  Console.WriteLine(temp);                  return temp;              };              Func<string> result = users.Aggregate(seed, next);              result();              Console.ReadLine();          }          }      static class Helper      {          public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)          {                TAccumulate local = seed;              foreach (TSource local2 in source)              {                  local = func(local, local2);              }              return local;          }        }  

 剩下的InvokeActionResultWithFilters方法和這裡的InvokeActionMethodWithFilters方法調用一樣

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.