ASP.NET core 的Filter是系統中經常用到的,本文詳細分享一下各種Filter定義、執行的內部機制以及執行順序。(ASP.NET Core 系列目錄)
一、 概述
ASP.NET Core MVC 中有好幾種常用的篩選器,例如Authorization filters 、Resource filters、Action filters 、Exception filters 、Result filters,他們運行在請求處理管道中的特定階段,例如Authorization filters當前請求的用戶是否已授權。 如果請求未獲授權,則中止執行后面的請求處理。其他幾種filters也類似,只是執行階段不同。如下圖:
圖一
Filter從定義到執行,本文通過四個階段說明,如下圖:
圖二
1.定義:以為例,可以通過繼承ActionFilterAttribute並override它的OnActionExecuting和OnActionExecuted方法實現。
2.注冊:主要有三種方式:在Startup的AddMvc、Controller、Action中注冊。
3.獲取:上一章有介紹,在確定了處理請求的Endpoint后,下一步就是創建創建invoker,它有個關鍵的屬性就是filters,它由FilterFactory的GetAllFilters方法獲取到。
4.執行:invoker的執行階段,會進入InvokeFilterPipelineAsync,在這里,各種Filter按照圖一的方式逐一被執行。
二、Filter的定義
Filter有好幾種,但由於本文主要是分享Filter的運行機制,所以只以ActionFilter一種來舉例,現在定義一個Test1Filter如下:
public class Test1Filter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { base.OnActionExecuting(context); //do..... } public override void OnActionExecuted(ActionExecutedContext context) { base.OnActionExecuted(context); //do...... } }
很簡單,可以很方便的通過繼承系統提供的ActionFilterAttribute並override 它的相應方法即可。
三、Filter的注冊
Filter定義好之后就是將其插入到處理管道中,可以在Startup的AddMvc、Controller、Action中注冊。
1.全局:在Startup的AddMvc中注冊
services.AddMvc( options => { options.Filters.Add(new Test6Filter()); options.Filters.Add(new Test4Filter()); } ).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
2.只對指定的Controller生效
[Test5Filter] [Test3Filter] public class FlyLoloController : Controller
3.只對指定的Action生效
[Test2Filter] [Test1Filter] public JsonResult Index()
在實際業務中,我們可以根據具體的需求來確定Filter的作用范圍。
四、Filter的獲取
Filter的獲取是在FilterFactory的GetAllFilters方法中,
public static FilterFactoryResult GetAllFilters(IFilterProvider[] filterProviders, ActionContext actionContext) { //省略…… var orderedFilters = actionDescriptor.FilterDescriptors.OrderBy(filter => filter,FilterDescriptorOrderComparer.Comparer).ToList(); //省略.... return new FilterFactoryResult(staticFilterItems, filters); }
保留了關鍵的一句話,那就是根據actionDescriptor來獲取到它對應的所有Filter(無論是針對全局、Controller還是Action),然后對這些Filter進行排序,這里用到了排序方法FilterDescriptorOrderComparer,它用來定義Filter的執行順序,詳細內容見后文。
五、Filter的執行
Filter的執行在invoker的執行階段,會進入InvokeFilterPipelineAsync,在這里,各種Filter按照圖一的方式逐一被執行。具體內容上一章已經進行了詳細的描述。它是通過兩個while循環實現了如圖一的順序逐一執行。
while (!isCompleted) { await Next(ref next, ref scope, ref state, ref isCompleted); }
具體不再贅述。
六、Filter的執行順序
Filter的執行順序由三部分決定:
1.對於不同種的Filter,按照圖一的順序執行,例如Authorization filters會最先被執行。
2.對於同種的Filter,執行順序由其Order和Scope來決定。
在Filter的獲取一節提到了Filter的排序方法FilterDescriptorOrderComparer,它擁有對Filter定的排序。
public class FilterDescriptorOrderComparer : IComparer<FilterDescriptor> { public static FilterDescriptorOrderComparer Comparer { get; } = new FilterDescriptorOrderComparer(); public int Compare(FilterDescriptor x, FilterDescriptor y) { if (x == null) { throw new ArgumentNullException(nameof(x)); } if (y == null) { throw new ArgumentNullException(nameof(y)); } if (x.Order == y.Order) { return x.Scope.CompareTo(y.Scope); } else { return x.Order.CompareTo(y.Order); } } }
從這個方法可以看到Filter的執行順序,按照先Order后Scope的方式排序。對於繼承默認的內置Filter的,Order默認為0,所有對於這樣的Filter來說覺得他們順序的是Scope,也就是作用域,默認情況下,全局的為10、Controller上的為20、Action上的為30.也就是說,Filter的執行順序為
全局 -> Controller -> Action, 實際的執行順序是這樣的:
全局 OnActionExecuting
Controller OnActionExecuting
Action OnActionExecuting
Action OnActionExecuted
Controller OnActionExecuted
全局 OnActionExecuted
也是嵌套的,和中間件的處理方式類似。
當然我們可以自定義Filter的Order使其不再采用默認值0,只需在其構造函數中設置即可
public class Test1Filter : ActionFilterAttribute { public Test1Filter() { Order = 1; } //........... }
3.對於同樣作用域的同種Filter來說,它們的執行順序是按照注冊先后排列的。
例如:
[Test2Filter] [Test1Filter] public JsonResult Index()
則先執行Test2Filter、后執行Test1Filter。