在上一篇中咱們講到控制器的執行過程系列,這個系列要擱置一段時間了,由於在控制器執行的過程當中包含的信息都是要單獨的用一個系列來描述的,就現在天的這個篇幅就是在上面內容以後所看到的一個知識要點之一。api
下面就來說解一下在ASP.NET Web API框架中過濾器的建立、執行過程。瀏覽器
過濾器所在的位置服務器
圖1框架
圖1所示的就是控制器執行過程很粗略的表示。異步
經過上一篇內容咱們瞭解到控制器方法選擇器最後返回的並非控制器方法,而是對於控制器方法描述的類型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切信息,今天要講的就是HttpActionDescriptor對象中生成的過濾器管道執行的這麼一個順序,固然其中就已經包含了建立的時候。ide
在介紹HttpActionDescriptor類型生成過濾器管道以前,咱們先來對着其中會涉及到的一些類型進行一個基礎的瞭解。函數
基礎類型一覽ui
FilterInfo 過濾器對象封裝信息(System.Web.Http.Filters)this
示例代碼1-1spa
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/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面