ASP.NET Web API 過濾器創建、執行過程(一)
前言
在上一篇中我們講到控制器的執行過程系列,這個系列要擱置一段時間了,因爲在控制器執行的過程中包含的信息都是要單獨的用一個系列來描述的,就如今天的這個篇幅就是在上面內容之後所看到的一個知識要點之一。
ASP.NETWeb API 過濾器創建、執行過程(一)
下面就來講解一下在ASP.NET Web API框架中過濾器的創建、執行過程。
過濾器所在的位置
圖1
圖1所示的就是控制器執行過程很粗略的表示。
通過上一篇內容我們瞭解到控制器方法選擇器最後返回的並不是控制器方法,而是對於控制器方法描述的類型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切信息,今天要講的就是HttpActionDescriptor對象中生成的過濾器管道執行的這麼一個順序,當然其中就已經包含了創建的時候。
在介紹HttpActionDescriptor類型生成過濾器管道之前,我們先來對着其中會涉及到的一些類型進行一個基礎的瞭解。
基礎類型一覽
FilterInfo 過濾器對象封裝信息(System.Web.Http.Filters)
示例代碼1-1
publicsealedclassFilterInfo { publicFilterInfo(IFilterinstance, FilterScopescope); publicIFilterInstance { get; } publicFilterScopeScope { get; } }
在代碼1-1中想必大家也看到了,FilterInfo類型中有兩屬性,一個是有着過濾器類型的實例對象的引用也就是IFilter類型的Instance屬性,還有一個是FilterScope類型的Scope屬性表示當前這個過濾器在項目中的應用範圍,這個值很重要,在過濾器管道中可是根據這個值來排序的。
FilterScope 過濾器應用範圍(System.Web.Http.Filters)
示例代碼1-2
publicenumFilterScope { // 摘要: // 在 Controller 之前指定一個操作。 Global=0, // // 摘要: // 在 Action 之前和 Global 之後指定一個順序。 Controller=10, // // 摘要: // 在 Controller 之後指定一個順序。 Action=20, }
從代碼1-2中一目瞭然了,這裏就不多說了。
IFilter 過濾器頂層接口(System.Web.Http.Filters)
示例代碼1-3
publicinterfaceIFilter { // 摘要: // 獲取或設置一個值,該值指示是否可以爲單個程序元素指定多個已指示特性的實例。 // // 返回結果: // 如果可以指定多個實例,則爲 true;否則爲 false。默認值爲 false。 boolAllowMultiple { get; } }
FilterAttribute過濾器默認實現特性類(System.Web.Http.Filters)
示例代碼1-4
// 摘要: // 表示操作-篩選器特性的基類。 [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited=true, AllowMultiple=true)] publicabstractclassFilterAttribute : Attribute, IFilter { // 摘要: // 初始化 System.Web.Http.Filters.FilterAttribute 類的新實例。 protectedFilterAttribute(); // 摘要: // 獲取用於指示是否允許多個篩選器的值。 // // 返回結果: // 如果允許多個篩選器,則爲 true;否則爲 false。 publicvirtualboolAllowMultiple { get; } }
示例代碼1-4中我們可以看到FilterAttribute類型爲過濾器默認的特性類型,而我們如果要想實現自定義的過濾器僅僅靠繼承自FilterAttribute是不行的,因爲FilterAttribute特性類只是屬於過濾器概念中的“屬性”,而過濾器中的行爲則是由過濾器類型來控制器的,也就是下面要說的。
IActionFilter 行爲過濾器接口(System.Web.Http.Filters)
示例代碼1-5
publicinterfaceIActionFilter : IFilter { // 摘要: // 異步執行篩選器操作。 // // 參數: // actionContext: // 操作上下文。 // // cancellationToken: // 爲此任務分配的取消標記。 // // continuation: // 在調用操作方法之後,委託函數將繼續。 // // 返回結果: // 爲此操作新建的任務。 Task<System.Net.Http.HttpResponseMessage>ExecuteActionFilterAsync(HttpActionContextactionContext, CancellationTokencancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>>continuation); }
這裏暫時不對行爲過濾器接口類型做什麼講解,在最後的示例中會有運用到,而對於剩下的驗證過濾器和異常過濾器接口也都差不多了,這裏就單獨的演示一個行爲過濾器。
過濾器管道生成過程
這裏我們還是要說到之前說過的HttpActionDescriptor類型,我來說一下大概的過程,首先呢在HttpActionDescriptor類型中有個內部字段叫_filterPipeline,從命名上就可以看出來大概的意思了,對的,過濾器管道的信息就是在這個字段中的,而它是個Lazy<Collection<FilterInfo>>類型。
在ApiController的執行中有個私有類型是FilterGrouping類型,它這個類型的作用是對過濾器管道中的所有過濾器進行分類,就是驗證歸驗證的,行爲是行爲的。
而實例化FilterGrouping類型的時候構造函數需要Collection<FilterInfo>類型的參數(也就是過濾器管道)來進行實例化,這個時候就是HttpActionDescriptor類型一展身手的時候了,看代碼1-6就是HttpActionDescriptor類型重的函數。
示例代碼1-6
publicvirtualCollection<FilterInfo>GetFilterPipeline() { returnthis._filterPipeline.Value; }
在上面我們也說到了_filterPipeline這個字段,那麼它的Value在這個時候做了什麼呢?
代碼1-6.1
this._filterPipeline= newLazy<Collection<FilterInfo>>(newFunc<Collection<FilterInfo>>(this.InitializeFilterPipeline));
看到這裏我們只需要關注InitializeFilterPipeline這個函數的實現。
代碼1-6.2
privateCollection<FilterInfo>InitializeFilterPipeline() { returnnewCollection<FilterInfo>(RemoveDuplicates((fromfpinthis._configuration.Services.GetFilterProviders() selectfp.GetFilters(this._configuration, this)).OrderBy<FilterInfo, FilterInfo>(f=>f, FilterInfoComparer.Instance).Reverse<FilterInfo>()).Reverse<FilterInfo>().ToList<FilterInfo>()); }
首先我們從這裏看到的是,會從HttpConfiguration中的服務容器中獲取基礎服務,也就是實現了IFilterProvider的服務,而在其中也是有兩個過濾器提供程序,下面我直接貼上源碼
示例代碼1-7
publicclassConfigurationFilterProvider : IFilterProvider { //Methods publicIEnumerable<FilterInfo>GetFilters(HttpConfigurationconfiguration, HttpActionDescriptoractionDescriptor) { if (configuration==null) { throwError.ArgumentNull("configuration"); } returnconfiguration.Filters; } }
在代碼1-7中返回的就是HttpConfiguration中的Filters屬性中的值。這裏瞭解一下繼續往下看,代碼1-8
publicclassActionDescriptorFilterProvider : IFilterProvider { //Methods publicIEnumerable<FilterInfo>GetFilters(HttpConfigurationconfiguration, HttpActionDescriptoractionDescriptor) { if (configuration==null) { throwError.ArgumentNull("configuration"); } if (actionDescriptor==null) { throwError.ArgumentNull("actionDescriptor"); } IEnumerable<FilterInfo>first=frominstanceinactionDescriptor.ControllerDescriptor.GetFilters() selectnewFilterInfo(instance, FilterScope.Controller); IEnumerable<FilterInfo>second=frominstanceinactionDescriptor.GetFilters() selectnewFilterInfo(instance, FilterScope.Action); returnfirst.Concat<FilterInfo>(second); } }
在代碼1-7中返回的是註冊在全局範圍使用的過濾器,而在代碼1-8中則是控制器和控制器方法範圍的過濾器。
這個時候我們再看代碼1-6.2中的FilterInfoComparer.Instance的默認實現。
代碼1-9
publicintCompare(FilterInfox, FilterInfoy) { if ((x==null) && (y==null)) { return0; } if (x==null) { return-1; } if (y==null) { return1; } return (int) (x.Scope-y.Scope); }
過濾器管道生成結果示例
看到這裏大家想必已經知道了返回的過濾器管道中是什麼樣子的了吧,如果不清楚也沒關係,下面的示例來說明一下。
同種類型過濾器覆蓋面的執行優先級:
服務端(SelfHost):
代碼1-10
usingSystem.Web.Http.Controllers; usingSystem.Web.Http.Filters; usingNameSpaceControllerThree; namespaceSelfHost { classProgram { staticvoidMain(string[] args) { HttpSelfHostConfigurationselfHostConfiguration= newHttpSelfHostConfiguration("http://localhost/selfhost"); using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional }); selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver), newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver()); //添加全局過濾器 selfHostServer.Configuration.Filters.Add(newWebAPIController.Filter.CustomConfigurationActionFilterAttribute()); selfHostServer.OpenAsync(); Console.WriteLine("服務器端服務監聽已開啓"); Console.Read(); } } } }
在服務端我們在HttpConfiguration中的Fileters屬性中添加上了WebAPIController.Filter.CustomConfigurationActionFilterAttribute這個類型,下面會有說到。
在WebAPIController項目中我會定義有控制器,以及同種過濾器的三種應用範圍(用類型名稱來區別了)。
首先我們看一下控制器類型的定義:
代碼1-11
namespaceNameSpaceControllerThree { [CustomControllerActionFilter] publicclassWriterAndReadController : ApiController { [CustomActionFilter] publicstringGet() { StringBuilderstrBuilder=newStringBuilder(); HttpActionDescriptoractionDescriptor=this.Configuration.Services.GetActionSelector().SelectAction(this.ControllerContext); System.Collections.ObjectModel.Collection<FilterInfo>filtersInfo=actionDescriptor.GetFilterPipeline(); foreach (varfilterinfiltersInfo) { strBuilder.AppendLine("【FilterName:"+filter.Instance.GetType().Name+",FilterScope:"+filter.Scope.ToString()+"】"); } returnstrBuilder.ToString(); } } }
可能看到這裏對於下面自定義的行爲過濾器會很感興趣,那麼就一起來看一下吧。
代碼1-12
publicclassCustomConfigurationActionFilterAttribute : FilterAttribute, IActionFilter { publicTask<System.Net.Http.HttpResponseMessage>ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContextactionContext, System.Threading.CancellationTokencancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>>continuation) { //Console.WriteLine(this.GetType().Name); returncontinuation(); } } publicclassCustomControllerActionFilterAttribute : FilterAttribute, IActionFilter { publicTask<System.Net.Http.HttpResponseMessage>ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContextactionContext, System.Threading.CancellationTokencancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>>continuation) { //Console.WriteLine(this.GetType().Name); returncontinuation(); } } publicclassCustomActionFilterAttribute : FilterAttribute, IActionFilter { publicTask<System.Net.Http.HttpResponseMessage>ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContextactionContext, System.Threading.CancellationTokencancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>>continuation) { //Console.WriteLine(this.GetType().Name); returncontinuation(); } }
我這裏是定義的三個行爲過濾器,在默認實現中爲了方便直接是調用continuation委託來進行操作,便於外部的疊加器使用。
這個時候我們在運行起來服務端過後,不管是通過瀏覽器訪問還是客戶端訪問都可以看到如下的結果圖。
圖2