天天看點

ASP.NET Web API 過濾器建立、執行過程(一)ASP.NET Web API 過濾器建立、執行過程(一)

前言

在上一篇中我們講到控制器的執行過程系列,這個系列要擱置一段時間了,因為在控制器執行的過程中包含的資訊都是要單獨的用一個系列來描述的,就如今天的這個篇幅就是在上面内容之後所看到的一個知識要點之一。

ASP.NETWeb API 過濾器建立、執行過程(一)

下面就來講解一下在ASP.NET Web API架構中過濾器的建立、執行過程。

過濾器所在的位置

圖1

<a href="http://s3.51cto.com/wyfs02/M02/48/61/wKiom1QHM6yAodThAAMGaQj_SgA431.jpg" target="_blank"></a>

圖1所示的就是控制器執行過程很粗略的表示。

通過上一篇内容我們了解到控制器方法選擇器最後傳回的并不是控制器方法,而是對于控制器方法描述的類型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切資訊,今天要講的就是HttpActionDescriptor對象中生成的過濾器管道執行的這麼一個順序,當然其中就已經包含了建立的時候。

在介紹HttpActionDescriptor類型生成過濾器管道之前,我們先來對着其中會涉及到的一些類型進行一個基礎的了解。

基礎類型一覽

FilterInfo 過濾器對象封裝資訊(System.Web.Http.Filters)

示例代碼1-1

1

2

3

4

5

6

7

<code>   </code><code>publicsealedclassFilterInfo</code>

<code>   </code><code>{</code>

<code>        </code><code>publicFilterInfo(IFilterinstance, FilterScopescope);</code>

<code> </code> 

<code>        </code><code>publicIFilterInstance { </code><code>get</code><code>; }</code>

<code>        </code><code>publicFilterScopeScope { </code><code>get</code><code>; }</code>

<code>   </code><code>}</code>

在代碼1-1中想必大家也看到了,FilterInfo類型中有兩屬性,一個是有着過濾器類型的執行個體對象的引用也就是IFilter類型的Instance屬性,還有一個是FilterScope類型的Scope屬性表示目前這個過濾器在項目中的應用範圍,這個值很重要,在過濾器管道中可是根據這個值來排序的。

FilterScope 過濾器應用範圍(System.Web.Http.Filters)

示例代碼1-2

8

9

10

11

12

13

14

<code>   </code><code>publicenumFilterScope</code>

<code>        </code><code>// 摘要:</code>

<code>        </code><code>//     在 Controller 之前指定一個操作。</code>

<code>        </code><code>Global=0,</code>

<code>        </code><code>//</code>

<code>        </code><code>//     在 Action 之前和 Global 之後指定一個順序。</code>

<code>        </code><code>Controller=10,</code>

<code>        </code><code>//     在 Controller 之後指定一個順序。</code>

<code>        </code><code>Action=20,</code>

從代碼1-2中一目了然了,這裡就不多說了。

IFilter 過濾器頂層接口(System.Web.Http.Filters)

示例代碼1-3

<code>   </code><code>publicinterfaceIFilter</code>

<code>        </code><code>//     擷取或設定一個值,該值訓示是否可以為單個程式元素指定多個已訓示特性的執行個體。</code>

<code>        </code><code>// 傳回結果:</code>

<code>        </code><code>//     如果可以指定多個執行個體,則為 true;否則為 false。預設值為 false。</code>

<code>        </code><code>boolAllowMultiple { </code><code>get</code><code>; }</code>

FilterAttribute過濾器預設實作特性類(System.Web.Http.Filters)

示例代碼1-4

15

16

<code>   </code><code>// 摘要:</code>

<code>   </code><code>//     表示操作-篩選器特性的基類。</code>

<code>   </code><code>[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited=</code><code>true</code><code>, AllowMultiple=</code><code>true</code><code>)]</code>

<code>   </code><code>publicabstractclassFilterAttribute : Attribute, IFilter</code>

<code>        </code><code>//     初始化 System.Web.Http.Filters.FilterAttribute 類的新執行個體。</code>

<code>        </code><code>protectedFilterAttribute();</code>

<code>        </code><code>//     擷取用于訓示是否允許多個篩選器的值。</code>

<code>        </code><code>//     如果允許多個篩選器,則為 true;否則為 false。</code>

<code>        </code><code>publicvirtualboolAllowMultiple { </code><code>get</code><code>; }</code>

示例代碼1-4中我們可以看到FilterAttribute類型為過濾器預設的特性類型,而我們如果要想實作自定義的過濾器僅僅靠繼承自FilterAttribute是不行的,因為FilterAttribute特性類隻是屬于過濾器概念中的“屬性”,而過濾器中的行為則是由過濾器類型來控制器的,也就是下面要說的。

IActionFilter 行為過濾器接口(System.Web.Http.Filters)

示例代碼1-5

17

18

19

<code>   </code><code>publicinterfaceIActionFilter : IFilter</code>

<code>        </code><code>//     異步執行篩選器操作。</code>

<code>        </code><code>// 參數:</code>

<code>        </code><code>//   actionContext:</code>

<code>        </code><code>//     操作上下文。</code>

<code>        </code><code>//   cancellationToken:</code>

<code>        </code><code>//     為此任務配置設定的取消标記。</code>

<code>        </code><code>//   continuation:</code>

<code>        </code><code>//     在調用操作方法之後,委托函數将繼續。</code>

<code>        </code><code>//     為此操作建立的任務。</code>

<code>        </code><code>Task&lt;System.Net.Http.HttpResponseMessage&gt;ExecuteActionFilterAsync(HttpActionContextactionContext, CancellationTokencancellationToken, Func&lt;Task&lt;System.Net.Http.HttpResponseMessage&gt;&gt;continuation);</code>

<code>    </code><code>}</code>

這裡暫時不對行為過濾器接口類型做什麼講解,在最後的示例中會有運用到,而對于剩下的驗證過濾器和異常過濾器接口也都差不多了,這裡就單獨的示範一個行為過濾器。

過濾器管道生成過程

這裡我們還是要說到之前說過的HttpActionDescriptor類型,我來說一下大概的過程,首先呢在HttpActionDescriptor類型中有個内部字段叫_filterPipeline,從命名上就可以看出來大概的意思了,對的,過濾器管道的資訊就是在這個字段中的,而它是個Lazy&lt;Collection&lt;FilterInfo&gt;&gt;類型。

在ApiController的執行中有個私有類型是FilterGrouping類型,它這個類型的作用是對過濾器管道中的所有過濾器進行分類,就是驗證歸驗證的,行為是行為的。

而執行個體化FilterGrouping類型的時候構造函數需要Collection&lt;FilterInfo&gt;類型的參數(也就是過濾器管道)來進行執行個體化,這個時候就是HttpActionDescriptor類型一展身手的時候了,看代碼1-6就是HttpActionDescriptor類型重的函數。

示例代碼1-6

<code>   </code><code>publicvirtualCollection&lt;FilterInfo&gt;GetFilterPipeline()</code>

<code>        </code><code>returnthis._filterPipeline.Value;</code>

在上面我們也說到了_filterPipeline這個字段,那麼它的Value在這個時候做了什麼呢?

代碼1-6.1

<code>   </code><code>this</code><code>._filterPipeline= newLazy&lt;Collection&lt;FilterInfo&gt;&gt;(newFunc&lt;Collection&lt;FilterInfo&gt;&gt;(</code><code>this</code><code>.InitializeFilterPipeline));</code>

看到這裡我們隻需要關注InitializeFilterPipeline這個函數的實作。

代碼1-6.2

<code>   </code><code>privateCollection&lt;FilterInfo&gt;InitializeFilterPipeline()</code>

<code>        </code><code>returnnewCollection&lt;FilterInfo&gt;(RemoveDuplicates((fromfpinthis._configuration.Services.GetFilterProviders() selectfp.GetFilters(</code><code>this</code><code>._configuration, </code><code>this</code><code>)).OrderBy&lt;FilterInfo, FilterInfo&gt;(f=&gt;f, FilterInfoComparer.Instance).Reverse&lt;FilterInfo&gt;()).Reverse&lt;FilterInfo&gt;().ToList&lt;FilterInfo&gt;());</code>

首先我們從這裡看到的是,會從HttpConfiguration中的服務容器中擷取基礎服務,也就是實作了IFilterProvider的服務,而在其中也是有兩個過濾器提供程式,下面我直接貼上源碼

示例代碼1-7

<code>   </code><code>publicclassConfigurationFilterProvider : IFilterProvider</code>

<code>        </code><code>//Methods</code>

<code>        </code><code>publicIEnumerable&lt;FilterInfo&gt;GetFilters(HttpConfigurationconfiguration, HttpActionDescriptoractionDescriptor)</code>

<code>        </code><code>{</code>

<code>            </code><code>if</code> <code>(configuration==</code><code>null</code><code>)</code>

<code>            </code><code>{</code>

<code>                </code><code>throwError.ArgumentNull(</code><code>"configuration"</code><code>);</code>

<code>            </code><code>}</code>

<code>            </code><code>returnconfiguration.Filters;</code>

<code>        </code><code>}</code>

在代碼1-7中傳回的就是HttpConfiguration中的Filters屬性中的值。這裡了解一下繼續往下看,代碼1-8

<code>   </code><code>publicclassActionDescriptorFilterProvider : IFilterProvider</code>

<code>            </code><code>if</code> <code>(actionDescriptor==</code><code>null</code><code>)</code>

<code>                </code><code>throwError.ArgumentNull(</code><code>"actionDescriptor"</code><code>);</code>

<code>            </code><code>IEnumerable&lt;FilterInfo&gt;first=frominstanceinactionDescriptor.ControllerDescriptor.GetFilters() selectnewFilterInfo(instance, FilterScope.Controller);</code>

<code>            </code><code>IEnumerable&lt;FilterInfo&gt;second=frominstanceinactionDescriptor.GetFilters() selectnewFilterInfo(instance, FilterScope.Action);</code>

<code>            </code><code>returnfirst.Concat&lt;FilterInfo&gt;(second);</code>

在代碼1-7中傳回的是注冊在全局範圍使用的過濾器,而在代碼1-8中則是控制器和控制器方法範圍的過濾器。

這個時候我們再看代碼1-6.2中的FilterInfoComparer.Instance的預設實作。

代碼1-9

<code>   </code><code>publicintCompare(FilterInfox, FilterInfoy)</code>

<code>        </code><code>if</code> <code>((x==</code><code>null</code><code>) &amp;&amp; (y==</code><code>null</code><code>))</code>

<code>            </code><code>return0;</code>

<code>        </code><code>if</code> <code>(x==</code><code>null</code><code>)</code>

<code>            </code><code>return</code><code>-1;</code>

<code>        </code><code>if</code> <code>(y==</code><code>null</code><code>)</code>

<code>            </code><code>return1;</code>

<code>        </code><code>return</code> <code>(</code><code>int</code><code>) (x.Scope-y.Scope);</code>

過濾器管道生成結果示例

看到這裡大家想必已經知道了傳回的過濾器管道中是什麼樣子的了吧,如果不清楚也沒關系,下面的示例來說明一下。

同種類型過濾器覆寫面的執行優先級:

服務端(SelfHost):

代碼1-10

20

21

22

23

24

25

26

27

28

29

30

<code>usingSystem.Web.Http.Controllers;</code>

<code>usingSystem.Web.Http.Filters;</code>

<code>usingNameSpaceControllerThree;</code>

<code>namespaceSelfHost</code>

<code>{</code>

<code>   </code><code>classProgram</code>

<code>        </code><code>staticvoidMain(</code><code>string</code><code>[] args)</code>

<code>           </code> 

<code>            </code><code>HttpSelfHostConfigurationselfHostConfiguration=</code>

<code>                </code><code>newHttpSelfHostConfiguration(</code><code>"http://localhost/selfhost"</code><code>);</code>

<code>            </code><code>using</code> <code>(HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration))</code>

<code>                </code><code>selfHostServer.Configuration.Routes.MapHttpRoute(</code>

<code>                    </code><code>"DefaultApi"</code><code>, </code><code>"api/{controller}/{id}"</code><code>, </code><code>new</code> <code>{ id=RouteParameter.Optional });</code>

<code>                </code><code>selfHostServer.Configuration.Services.Replace(</code><code>typeof</code><code>(IAssembliesResolver),</code>

<code>                    </code><code>newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());</code>

<code>                </code><code>//添加全局過濾器</code>

<code>                </code><code>selfHostServer.Configuration.Filters.Add(newWebAPIController.Filter.CustomConfigurationActionFilterAttribute());</code>

<code>                </code><code>selfHostServer.OpenAsync();</code>

<code>                </code><code>Console.WriteLine(</code><code>"伺服器端服務監聽已開啟"</code><code>);</code>

<code>                </code><code>Console.Read();</code>

<code>}</code>

在服務端我們在HttpConfiguration中的Fileters屬性中添加上了WebAPIController.Filter.CustomConfigurationActionFilterAttribute這個類型,下面會有說到。

在WebAPIController項目中我會定義有控制器,以及同種過濾器的三種應用範圍(用類型名稱來差別了)。

首先我們看一下控制器類型的定義:

代碼1-11

<code>namespaceNameSpaceControllerThree</code>

<code>   </code><code>[CustomControllerActionFilter]</code>

<code>   </code><code>publicclassWriterAndReadController : ApiController</code>

<code>        </code><code>[CustomActionFilter]</code>

<code>        </code><code>publicstringGet()</code>

<code>            </code><code>StringBuilderstrBuilder=newStringBuilder();</code>

<code>            </code><code>HttpActionDescriptoractionDescriptor=</code><code>this</code><code>.Configuration.Services.GetActionSelector().SelectAction(</code><code>this</code><code>.ControllerContext);</code>

<code>            </code><code>System.Collections.ObjectModel.Collection&lt;FilterInfo&gt;filtersInfo=actionDescriptor.GetFilterPipeline();</code>

<code>            </code><code>foreach</code> <code>(varfilterinfiltersInfo)</code>

<code>                </code><code>strBuilder.AppendLine(</code><code>"【FilterName:"</code><code>+filter.Instance.GetType().Name+</code><code>",FilterScope:"</code><code>+filter.Scope.ToString()+</code><code>"】"</code><code>);</code>

<code>            </code><code>returnstrBuilder.ToString();</code>

可能看到這裡對于下面自定義的行為過濾器會很感興趣,那麼就一起來看一下吧。

代碼1-12

<code>   </code><code>publicclassCustomConfigurationActionFilterAttribute : FilterAttribute, IActionFilter</code>

<code>        </code><code>publicTask&lt;System.Net.Http.HttpResponseMessage&gt;ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContextactionContext, System.Threading.CancellationTokencancellationToken, Func&lt;Task&lt;System.Net.Http.HttpResponseMessage&gt;&gt;continuation)</code>

<code>            </code><code>//Console.WriteLine(this.GetType().Name);</code>

<code>            </code><code>returncontinuation();</code>

<code>   </code><code>publicclassCustomControllerActionFilterAttribute : FilterAttribute, IActionFilter</code>

<code>   </code><code>publicclassCustomActionFilterAttribute : FilterAttribute, IActionFilter</code>

我這裡是定義的三個行為過濾器,在預設實作中為了友善直接是調用continuation委托來進行操作,便于外部的疊加器使用。

這個時候我們在運作起來服務端過後,不管是通過浏覽器通路還是用戶端通路都可以看到如下的結果圖。

圖2

<a href="http://s3.51cto.com/wyfs02/M00/48/61/wKiom1QHNGeSt7MkAAMFaIyeAGQ131.jpg" target="_blank"></a>

     本文轉自jinyuan0829 51CTO部落格,原文連結:http://blog.51cto.com/jinyuan/1548571,如需轉載請自行聯系原作者

繼續閱讀