ASP.NET MVC 幾種 Filter 的執行過程原始碼解析
一、前言
之前也閱讀過MVC的原始碼,並瞭解過各個模組的執行原理和執行過程,但都沒有形成文章(所以也忘得特別快),總感覺分析原始碼是大神的工作,而且很多人覺得平時根本不需要知道這些,會用就行了。其實閱讀原始碼是個很好的習慣,它不只停留在知道怎麼用的階段,而是讓我們知道一系列的為什麼,為什麼這樣設計,為什麼這樣使用…。很多朋友應該看過《asp.net x 框架揭祕》這本書,確實不錯,特別是邊看原始碼邊看書,可以有不小的收穫。Ok,我不是大神,我只是心血來潮想看一下原始碼!
二、幾種常見的Filter
說到mvc裡的Filter,自然會想到IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter,搜尋一下也都知道怎麼用了。其實說白了,這些介面定義了一系列方法,這些方法在請求的不同時機被執行,所謂Filter,就是讓我們可以在不同時機進行攔截處理。
這裡還涉及到一個特性:FilterAttribute,例如常用的AuthorizeAttribute就繼承了FilterAttribute和實現了IAuthorizationFilter介面。說到Attribute,馬上會關聯到:執行時、反射、效能。框架會在執行過程中,通過反射獲取標記屬性,並執行特定的操作;至於效能問題,通常可以通過快取來優化。
所以,我們可以做出猜測,以AuthorizeAttribute為例,msdn說它可以進行許可權驗證,也就是在Action執行前,框架會通過反射獲取標記在Action(或Controller)上的FilterAttribute,並執行IAuthorizationFilter定義的OnAuthorization方法,在該方法內部進行許可權驗證。所以如果我們要在Action執行前做某些判斷或處理,可以 1.定義一個Attribute繼承FilterAttribute,並實現IActionFilter介面(與IAuthorizationFilter不同的是,這個時候ModelBinding已經完成);2.實現IActionFilter中的方法;3.標記在Action(或Controller上)。ok,下面就通過原始碼來驗證這個過程。
三、原始碼分析
Action的執行是由ActionInvoker負責的,我們直接從這裡出發。IActionInvoker定義了ActionInvoker要實現的方法,該介面定義如下:
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方法內,就是根據這個分類,在各個時機進行相應的呼叫的。
四、總結
通過一張圖來簡單總結一下:
相關文章
- 原始碼分析OKHttp的執行過程原始碼HTTP
- ASP.NET執行過程- -ASP.NET
- ASP.NET MVC FilterASP.NETMVCFilter
- Spring MVC framework 執行過程SpringMVCFramework
- knockout原始碼分析之執行過程原始碼
- 【ASP.NET Core】MVC過濾器:執行流程ASP.NETMVC過濾器
- MapReduce 執行全過程解析
- RxJava原始碼解析(二)—執行緒排程器SchedulerRxJava原始碼執行緒
- laravel 應用層執行過程原始碼分析Laravel原始碼
- [zebra原始碼]分片語句ShardPreparedStatement執行過程原始碼
- Spring MVC 啟動過程原始碼分析SpringMVC原始碼
- Spark 原始碼系列(六)Shuffle 的過程解析Spark原始碼
- 原始碼簡析Spring-Integration執行過程原始碼Spring
- 走近原始碼:Redis命令執行過程(客戶端)原始碼Redis客戶端
- [ASP.NET MVC 小牛之路]11 - FilterASP.NETMVCFilter
- Mybatis原始碼分析(五)探究SQL語句的執行過程MyBatis原始碼SQL
- 面試必問-幾種執行緒安全的Map解析面試執行緒
- 瀏覽器EventLoop執行過程解析瀏覽器OOP
- 從Chrome原始碼看DNS解析過程Chrome原始碼DNS
- Dubbo服務呼叫過程原始碼解析④原始碼
- 以太坊啟動過程原始碼解析原始碼
- Lucene原始碼解析--搜尋過程<二>原始碼
- 從原始碼的角度解析執行緒池執行原理原始碼執行緒
- 執行緒池執行模型原始碼全解析執行緒模型原始碼
- Tomcat聯結器執行過程(原始碼閱讀)Tomcat原始碼
- Dubbo原始碼解析之服務呼叫過程原始碼
- Dubbo原始碼解析之服務引入過程原始碼
- android apk安裝過程原始碼解析AndroidAPK原始碼
- 看 Lumen 原始碼解析 Request 到 Response 過程原始碼
- SpringMVC原始碼解析(1)-啟動過程SpringMVC原始碼
- android View measure過程原始碼解析AndroidView原始碼
- 微信小程式 使用filter過濾器幾種方式微信小程式Filter過濾器
- 精盡MyBatis原始碼分析 - SQL執行過程(三)之 ResultSetHandlerMyBatis原始碼SQL
- 精盡MyBatis原始碼分析 - SQL執行過程(一)之 ExecutorMyBatis原始碼SQL
- 精盡MyBatis原始碼分析 - SQL執行過程(二)之 StatementHandlerMyBatis原始碼SQL
- 程式碼精簡執行過程
- Android原始碼完全解析——View的Measure過程Android原始碼View
- Spring Bean 的例項化過程原始碼解析SpringBean原始碼