標籤:self val 建立 route 過濾器 基礎類型 using list inf
前言
在上一篇中我們講到控制器的執行過程系列,這個系列要擱置一段時間了,因為在控制器執行的過程中包含的資訊都是要單獨的用一個系列來描述的,就如今天的這個篇幅就是在上面內容之後所看到的一個知識要點之一。
ASP.NET Web API 過濾器建立、執行過程(一)
下面就來講解一下在ASP.NET Web API架構中過濾器的建立、執行過程。
過濾器所在的位置
圖1
圖1所示的就是控制器執行過程很粗略的表示。
通過上一篇內容我們瞭解到控制器方法選取器最後返回的並不是控制器方法,而是對於控制器方法描述的類型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切資訊,今天要講的就是HttpActionDescriptor對象中產生的過濾器管道執行的這麼一個順序,當然其中就已經包含了建立的時候。
在介紹HttpActionDescriptor類型產生過濾器管道之前,我們先來對著其中會涉及到的一些類型進行一個基礎的瞭解。
基礎類型一覽
FilterInfo 過濾器對象封裝資訊(System.Web.Http.Filters)
範例程式碼1-1
public sealed class FilterInfo { public FilterInfo(IFilter instance, FilterScope scope); public IFilter Instance { get; } public FilterScope Scope { get; } }
在代碼1-1中想必大家也看到了,FilterInfo類型中有兩屬性,一個是有著過濾器類型的執行個體對象的引用也就是IFilter類型的Instance屬性,還有一個是FilterScope類型的Scope屬性工作表示當前這個過濾器在項目中的應用範圍,這個值很重要,在過濾器管道中可是根據這個值來排序的。
FilterScope 過濾器應用範圍(System.Web.Http.Filters)
範例程式碼1-2
public enum FilterScope { // 摘要: // 在 Controller 之前指定一個操作。 Global = 0, // // 摘要: // 在 Action 之前和 Global 之後指定一個順序。 Controller = 10, // // 摘要: // 在 Controller 之後指定一個順序。 Action = 20, }
從代碼1-2中一目瞭然了,這裡就不多說了。
IFilter 過濾器頂層介面(System.Web.Http.Filters)
範例程式碼1-3
public interface IFilter { // 摘要: // 擷取或設定一個值,該值指示是否可以為單個程式元素指定多個已指示特性的執行個體。 // // 返回結果: // 如果可以指定多個執行個體,則為 true;否則為 false。預設值為 false。 bool AllowMultiple { get; } }
FilterAttribute 過濾器預設實現屬性類別(System.Web.Http.Filters)
// 摘要: // 表示操作-篩選器特性的基類。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public abstract class FilterAttribute : Attribute, IFilter { // 摘要: // 初始化 System.Web.Http.Filters.FilterAttribute 類的新執行個體。 protected FilterAttribute(); // 摘要: // 擷取用於指示是否允許多個篩選器的值。 // // 返回結果: // 如果允許多個篩選器,則為 true;否則為 false。 public virtual bool AllowMultiple { get; } }
範例程式碼1-4中我們可以看到FilterAttribute類型為過濾器預設的屬性類別型,而我們如果要想實現自訂的過濾器僅僅靠繼承自FilterAttribute是不行的,因為FilterAttribute屬性類別只是屬於過濾器概念中的“屬性”,而過濾器中的行為則是由過濾器類型來控制器的,也就是下面要說的。
IActionFilter 行為過濾器介面(System.Web.Http.Filters)
範例程式碼1-5
public interface IActionFilter : IFilter { // 摘要: // 非同步執行篩選器操作。 // // 參數: // actionContext: // 操作上下文。 // // cancellationToken: // 為此任務分配的取消標幟。 // // continuation: // 在叫用作業方法之後,委託函數將繼續。 // // 返回結果: // 為此操作建立的任務。 Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation); }
這裡暫時不對行為過濾器介面類型做什麼講解,在最後的樣本中會有運用到,而對於剩下的驗證過濾器和異常過濾器介面也都差不多了,這裡就單獨的示範一個行為過濾器。
過濾器管道產生過程
這裡我們還是要說到之前說過的HttpActionDescriptor類型,我來說一下大概的過程,首先呢在HttpActionDescriptor類型中有個內部欄位叫_filterPipeline,從命名上就可以看出來大概的意思了,對的,過濾器管道的資訊就是在這個欄位中的,而它是個Lazy<Collection<FilterInfo>>類型。
在ApiController的執行中有個私人類型是FilterGrouping類型,它這個類型的作用是對過濾器管道中的所有過濾器進行分類,就是驗證歸驗證的,行為是行為的。
而執行個體化FilterGrouping類型的時候建構函式需要Collection<FilterInfo>類型的參數(也就是過濾器管道)來進行執行個體化,這個時候就是HttpActionDescriptor類型一展身手的時候了,看代碼1-6就是HttpActionDescriptor類型重的函數。
範例程式碼1-6
public virtual Collection<FilterInfo> GetFilterPipeline() { return this._filterPipeline.Value; }
在上面我們也說到了_filterPipeline這個欄位,那麼它的Value在這個時候做了什麼呢?
代碼1-6.1
this._filterPipeline = new Lazy<Collection<FilterInfo>>(new Func<Collection<FilterInfo>>(this.InitializeFilterPipeline));
看到這裡我們只需要關注InitializeFilterPipeline這個函數的實現。
代碼1-6.2
private Collection<FilterInfo> InitializeFilterPipeline() { return new Collection<FilterInfo>(RemoveDuplicates((from fp in this._configuration.Services.GetFilterProviders() select fp.GetFilters(this._configuration, this)).OrderBy<FilterInfo, FilterInfo>(f => f, FilterInfoComparer.Instance).Reverse<FilterInfo>()).Reverse<FilterInfo>().ToList<FilterInfo>()); }
首先我們從這裡看到的是,會從HttpConfiguration中的服務容器中擷取基礎服務,也就是實現了IFilterProvider的服務,而在其中也是有兩個過濾器提供者,下面我直接貼上源碼
範例程式碼1-7
public class ConfigurationFilterProvider : IFilterProvider { // Methods public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) { if (configuration == null) { throw Error.ArgumentNull("configuration"); } return configuration.Filters; } }
在代碼1-7中返回的就是HttpConfiguration中的Filters屬性中的值。這裡瞭解一下繼續往下看,
代碼1-8
public class ActionDescriptorFilterProvider : IFilterProvider { // Methods public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) { if (configuration == null) { throw Error.ArgumentNull("configuration"); } if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } IEnumerable<FilterInfo> first = from instance in actionDescriptor.ControllerDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Controller); IEnumerable<FilterInfo> second = from instance in actionDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Action); return first.Concat<FilterInfo>(second); } }
在代碼1-7中返回的是註冊在全域範圍使用的過濾器,而在代碼1-8中則是控制器和控制器方法範圍的過濾器。
這個時候我們再看代碼1-6.2中的FilterInfoComparer.Instance的預設實現。
代碼1-9
public int Compare(FilterInfo x, FilterInfo y) { if ((x == null) && (y == null)) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } return (int) (x.Scope - y.Scope); }
過濾器管道產生結果樣本
看到這裡大家想必已經知道了返回的過濾器管道中是什麼樣子的了吧,如果不清楚也沒關係,下面的樣本來說明一下。
同種類型過濾器覆蓋面的執行優先順序:
服務端(SelfHost):
代碼1-10
using System.Web.Http.Controllers;using System.Web.Http.Filters;using NameSpaceControllerThree;namespace SelfHost{ class Program { static void Main(string[] args) { HttpSelfHostConfiguration selfHostConfiguration = new HttpSelfHostConfiguration("http://localhost/selfhost"); using (HttpSelfHostServer selfHostServer = new HttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional }); selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver.LoadSpecifiedAssembliesResolver()); //添加全域過濾器 selfHostServer.Configuration.Filters.Add(new WebAPIController.Filter.CustomConfigurationActionFilterAttribute()); selfHostServer.OpenAsync(); Console.WriteLine("伺服器端服務監聽已開啟"); Console.Read(); } } }}
在服務端我們在HttpConfiguration中的Fileters屬性中添加上了WebAPIController.Filter.CustomConfigurationActionFilterAttribute這個類型,下面會有說到。
在WebAPIController項目中我會定義有控制器,以及同種過濾器的三種應用範圍(用類型名稱來區別了)。
首先我們看一下控制器類型的定義:
代碼1-11
namespace NameSpaceControllerThree{ [CustomControllerActionFilter] public class WriterAndReadController : ApiController { [CustomActionFilter] public string Get() { StringBuilder strBuilder = new StringBuilder(); HttpActionDescriptor actionDescriptor = this.Configuration.Services.GetActionSelector().SelectAction(this.ControllerContext); System.Collections.ObjectModel.Collection<FilterInfo> filtersInfo = actionDescriptor.GetFilterPipeline(); foreach (var filter in filtersInfo) { strBuilder.AppendLine("【FilterName:"+filter.Instance.GetType().Name+",FilterScope:"+filter.Scope.ToString()+"】"); } return strBuilder.ToString(); } }}
可能看到這裡對於下面自訂的行為過濾器會高度興趣,那麼就一起來看一下吧。
代碼1-12
public class CustomConfigurationActionFilterAttribute : FilterAttribute, IActionFilter { public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation) { //Console.WriteLine(this.GetType().Name); return continuation(); } } public class CustomControllerActionFilterAttribute : FilterAttribute, IActionFilter { public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation) { //Console.WriteLine(this.GetType().Name); return continuation(); } } public class CustomActionFilterAttribute : FilterAttribute, IActionFilter { public Task<System.Net.Http.HttpResponseMessage> ExecuteActionFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation) { //Console.WriteLine(this.GetType().Name); return continuation(); } }
我這裡是定義的三個行為過濾器,在預設實現中為了方便直接是調用continuation委託來進行操作,便於外部的疊加器使用。
這個時候我們在運行起來服務端過後,不管是通過瀏覽器訪問還是用戶端訪問都可以看到如下的結果圖。
圖2
金源
出處:http://www.cnblogs.com/jin-yuan/
ASP.NET Web API 過濾器建立、執行過程(一)