經過源碼瞭解ASP.NET MVC 幾種Filter的執行過程

1、前言緩存

  以前也閱讀過MVC的源碼,並瞭解過各個模塊的運行原理和執行過程,但都沒有造成文章(因此也忘得特別快),總感受分析源碼是大神的工做,並且不少人以爲平時根本不須要知道這些,會用就好了。其實閱讀源碼是個很好的習慣,它不僅停留在知道怎麼用的階段,而是讓咱們知道一系列的爲何,爲何這樣設計,爲何這樣使用...。不少朋友應該看過《asp.net x 框架揭祕》這本書,確實不錯,特別是邊看源碼邊看書,能夠有不小的收穫。Ok,我不是大神,我只是心血來潮想看一下源碼!mvc

2、幾種常見的Filter框架

  說到mvc裏的Filter,天然會想到IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter,搜索一下也都知道怎麼用了。其實說白了,這些接口定義了一系列方法,這些方法在請求的不一樣時機被執行,所謂Filter,就是讓咱們能夠在不一樣時機進行攔截處理。asp.net

     這裏還涉及到一個特性:FilterAttribute,例如經常使用的AuthorizeAttribute就繼承了FilterAttribute和實現了IAuthorizationFilter接口。說到Attribute,立刻會關聯到:運行時、反射、性能。框架會在運行過程當中,經過反射獲取標記屬性,並執行特定的操做;至於性能問題,一般能夠經過緩存來優化。ide

  因此,咱們能夠作出猜想,以AuthorizeAttribute爲例,msdn說它能夠進行權限驗證,也就是在Action執行前,框架會經過反射獲取標記在Action(或Controller)上的FilterAttribute,並執行IAuthorizationFilter定義的OnAuthorization方法,在該方法內部進行權限驗證。因此若是咱們要在Action執行前作某些判斷或處理,能夠 1.定義一個Attribute繼承FilterAttribute,並實現IActionFilter接口(與IAuthorizationFilter不一樣的是,這個時候ModelBinding已經完成);2.實現IActionFilter中的方法;3.標記在Action(或Controller上)。ok,下面就經過源碼來驗證這個過程。函數

3、源碼分析源碼分析

  Action的執行是由ActionInvoker負責的,咱們直接從這裏出發。IActionInvoker定義了ActionInvoker要實現的方法,該接口定義以下:  post

    public interface IActionInvoker
    {
        bool InvokeAction(ControllerContext controllerContext, string actionName);
    }

  ControllerActionInvoker  實現了該接口,顧名思義,它用於執行Controller 的 Action方法。它的 InvokeAction以下:性能

        public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
            ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
            if (actionDescriptor != null)
            {
                //標記1
                FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

                try
                {
                    //標記2
                    AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);

                    if (authenticationContext.Result != null)
                    {
                        InvokeActionResult(controllerContext, authenticationContext.Result);
                    }
                    else
                    {
                        IPrincipal principal = authenticationContext.Principal;

                        if (principal != null)
                        {
                            Thread.CurrentPrincipal = principal;
                            HttpContext.Current.User = principal;
                        }

                        //標記3
                        AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                        if (authorizationContext.Result != null)
                        {
                            AuthenticationChallengeContext challengeContext =
                                InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor, authorizationContext.Result);                           
                            InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
                        }
                        else
                        {
                            if (controllerContext.Controller.ValidateRequest)
                            {
                                ValidateRequest(controllerContext);
                            }

                            //標記4
                            IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
                            ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
                            //標記5
                            InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
                        }
                    }
                }
                catch (Exception ex)
                {
                    //標記6
                    ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
                    if (!exceptionContext.ExceptionHandled)
                    {
                        throw;
                    }
                    InvokeActionResult(controllerContext, exceptionContext.Result);
                }

                return true;
            }
            return false;
        }

  其實這裏的6個標記已經印證了咱們的猜想,先獲取各類Filter,而後在各個時機執行它們。上面標記2-6都是InvokeXXXFilters就是具體的執行方法。優化

  可是,到這裏上面咱們說到的FilterAttribute尚未出現。咱們先把焦點放到標記1,GetFilters 上,它獲取一個FilterInfo。GetFilters的定義以下:

        protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
        }

  _getFiltersThunk 是一個私有變量:

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

  經過定義能夠看出,_getFiltersThunk 會返回一個Filter 集合(這裏的Filter是一個實際的類,而上面提到的是概念性的東西,或者叫過濾器更合適),Filter 對象包裝了IXXXFilter接口對象,具體是在其Instance 屬性中。這裏有點繞,但不影響,簡單的說就是 GetFilters 方法會根據 FilterProviders.Providers.GetFilters 返回的一個IEnumerable<Filter>包裝一個 FilterInfo對象。

  咱們先看 IEnumerable<Filter> 是如何獲取的,它經過 FilterProviders.Providers.GetFilters 得到,FilterProviders 定義以下:

        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; }
        }

  這裏能夠註冊自定義的FilterProvider,FilterProvider實際是實現了IFilterProvider(定義了GetFilters方法)的類。能夠看到,mvc 默認已經準備兩個FilterProvider。調用GetFilters實際會遍歷每個FilterProvider的GetFilters方法,之內置的FilterAttributeFilterProvider 爲例,它的 GetFilters方法以下:

        public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            ControllerBase controller = controllerContext.Controller;
            var typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)
                .Select(attr => new Filter(attr, FilterScope.Controller, null));
            var methodFilters = GetActionAttributes(controllerContext, actionDescriptor)
                .Select(attr => new Filter(attr, FilterScope.Action, null));

            return typeFilters.Concat(methodFilters).ToList();
        }

  這裏也能夠看到,Filter對象包裝了具體的過濾器。其中GetControllerAttributes,實際它會調用ControllerDescriptor的 GetFilterAttribute,該方法定義以下:

        public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
        {
            return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
        }

  ok,FilterAttribute 終於出現了!GetActionAttributes 也是相似的過程。

     獲取到Controller和Action的FilterAttribute,幷包裝成Filter集合後,就會構建一個FilterInfo對象,該對象的做用能夠從其構造函數看出:

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

            var overrides = cache.Where(f => f.Instance is IOverrideFilter);

            FilterScope actionOverride = SelectLastScope<IActionFilter>(overrides);
            FilterScope authenticationOverride = SelectLastScope<IAuthenticationFilter>(overrides);
            FilterScope authorizationOverride = SelectLastScope<IAuthorizationFilter>(overrides);
            FilterScope exceptionOverride = SelectLastScope<IExceptionFilter>(overrides);
            FilterScope resultOverride = SelectLastScope<IResultFilter>(overrides);

            _actionFilters.AddRange(SelectAvailable<IActionFilter>(cache, actionOverride));
            _authenticationFilters.AddRange(SelectAvailable<IAuthenticationFilter>(cache, authenticationOverride));
            _authorizationFilters.AddRange(SelectAvailable<IAuthorizationFilter>(cache, authorizationOverride));
            _exceptionFilters.AddRange(SelectAvailable<IExceptionFilter>(cache, exceptionOverride));
            _resultFilters.AddRange(SelectAvailable<IResultFilter>(cache, resultOverride));
        }

  很明顯,它將Filter 按照各自IXXXFilter接口進行分類。在InvokeAction方法內,就是根據這個分類,在各個時機進行相應的調用的。

4、總結

  經過一張圖來簡單總結一下:

相關文章
相關標籤/搜索