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


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

前言

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

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/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM