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

來源:互聯網
上載者:User

標籤:async   attribute   定義   產生   程式碼分析   參數   can   select   nim   

前言

前面一篇中講解了過濾器執行之前的建立,通過實現IFilterProvider註冊到當前的HttpConfiguration裡的服務容器中,當然預設的基礎服務也是有的,並且根據這些提供者所獲得的的過濾器資訊集合進行排序。本篇就會對過濾器在建立完之後所做的一系列操作進行講解。

 

ASP.NET Web API 過濾器建立、執行過程(二) FilterGrouping 過濾器分組類型

FilterGrouping類型是ApiController類型中的私人類型,它的作用就如同它的命名一樣,用來對過濾器集合進行分組,在上一篇中我們看到,在經過調用HttpActionDescriptor類型的GetFilterPipeline()方法之後回去擷取到排序過後的過濾器資訊集合Collection<FilterInfo>。下面我們看一下FilterGrouping類型定義:

範例程式碼1-1

    private class FilterGrouping    {        // Fields        private List<IActionFilter> _actionFilters = new List<IActionFilter>();        private List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();        private List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();        // Methods        public FilterGrouping(IEnumerable<FilterInfo> filters)        {            foreach (FilterInfo info in filters)            {                IFilter instance = info.Instance;                Categorize<IActionFilter>(instance, this._actionFilters);                Categorize<IAuthorizationFilter>(instance, this._authorizationFilters);                Categorize<IExceptionFilter>(instance, this._exceptionFilters);            }        }        private static void Categorize<T>(IFilter filter, List<T> list) where T : class        {            T item = filter as T;            if (item != null)            {                list.Add(item);            }        }        // Properties        public IEnumerable<IActionFilter> ActionFilters        {            get            {                return this._actionFilters;            }        }        public IEnumerable<IAuthorizationFilter> AuthorizationFilters        {            get            {                return this._authorizationFilters;            }        }        public IEnumerable<IExceptionFilter> ExceptionFilters        {            get            {                return this._exceptionFilters;            }        }}

在代碼1-1中我們看到在FilterGrouping類型的建構函式中便會對過濾器資訊集合進行分組了,當然了分組的時候是調用FilterGrouping類型中的放吧,在Categorize()方法中就是根據執行個體的類型來進行判斷的,最後由FilterGrouping類型中的三個公用屬性來表示分組過後的不同類型的過濾器集合。

 

過濾器執行過程

在上個篇幅中我們通過樣本瞭解到過濾器管道的產生過程以及結果,我們就來看一下執行的過程,順帶再看下過濾器管道的結果是不是如上篇上所說的那樣。

先看伺服器端(Selfhost)的代碼:

代碼1-2

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();            }        }    }} 

這裡只有一個添加全域行為過濾器的這麼一句代碼,其餘的部分就不解釋了。

然後我們接著看控制器部分,如下範例程式碼:

代碼1-3

namespace NameSpaceControllerThree{    [CustomControllerAuthorizationFilter]    [CustomControllerActionFilter]    public class WriterAndReadController : ApiController    {        [CustomActionFilter]        [CustomControllerActionAuthorizationFilter]        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-4

/// <summary>    /// 全域的行為過濾器    /// </summary>    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();        }    }    /// <summary>    /// 控制器級行為過濾器    /// </summary>    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();        }    }    /// <summary>    /// 控制器方法級行為過濾器    /// </summary>    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();        }    }    /// <summary>    /// 控制器級授權訪問過濾器    /// </summary>    public class CustomControllerAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter    {        public Task<System.Net.Http.HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)        {            Console.WriteLine(this.GetType().Name);            return continuation();        }    }    /// <summary>    /// 控制器方法級授權訪問過濾器    /// </summary>    public class CustomControllerActionAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter    {        public Task<System.Net.Http.HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<Task<System.Net.Http.HttpResponseMessage>> continuation)        {            Console.WriteLine(this.GetType().Name);            return continuation();        }}

在代碼1-4中,我們可以看到代碼1-3中所有使用到的過濾器類型和代碼1-2中添加全域過濾器類型。

現在我們看一下最後的結果

圖1

黑色框的結果為SelfHost伺服器端過濾器執行過程的輸出,在代碼1-4中我們可以看到,這個得出的一個結論是授權過濾器不管是什麼應用範圍的都是優於行為過濾器的,而在同一種類型的過濾器中是根據應用範圍來確定執行順序的,這個跟下面的瀏覽器裡的內容有點關係,瀏覽器裡顯示的就是所有過濾器在排序後的管道裡的樣子,可以看到管道裡單純的就是按照應用範圍的層級來排序的,至於這個過濾器是什麼類型在處理排序的時候則是一點都不關心的。

 

過濾器執行過程 - 程式碼分析

首先看一下如下,可以代表了在控制器執行的過程中過濾器的執行過程。

圖2

上面通過樣本來說明了過濾器的執行過程,現在我們來看一下在架構的源碼中是什麼樣的,因為在過濾器執行過程中還包含了其它方面的知識點,所以這個是遲早都要看的,下面我們就來看一下吧。

代碼1-5

     return InvokeActionWithExceptionFilters(InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, () => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {            this._modelState = actionContext.ModelState;            return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();        }, new CancellationToken(), false))(), actionContext, cancellationToken, exceptionFilters);

在代碼1-5中涉及到三個靜態方法,我們先來看一下:

InvokeActionWithExceptionFilters()

InvokeActionWithAuthorizationFilters()

InvokeActionWithActionFilters()

範例程式碼如下:

代碼1-6

    internal static Task<HttpResponseMessage> InvokeActionWithExceptionFilters(Task<HttpResponseMessage> actionTask, HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IExceptionFilter> filters)    {        return actionTask.Catch<HttpResponseMessage>(delegate (CatchInfo<HttpResponseMessage> info) {            HttpActionExecutedContext executedContext = new HttpActionExecutedContext(actionContext, info.Exception);            filters = filters.Reverse<IExceptionFilter>();            IEnumerable<Task> asyncIterator = from filter in filters select filter.ExecuteExceptionFilterAsync(executedContext, cancellationToken);            bool runSynchronously = true;            Task<HttpResponseMessage> task = TaskHelpers.Iterate(asyncIterator, cancellationToken, true).Then<HttpResponseMessage>(delegate {                if (executedContext.Response != null)                {                    return TaskHelpers.FromResult<HttpResponseMessage>(executedContext.Response);                }                return TaskHelpers.FromError<HttpResponseMessage>(executedContext.Exception);            }, new CancellationToken(), runSynchronously);            return info.Task(task);        }, new CancellationToken());    }

代碼1-7

    internal static Func<Task<HttpResponseMessage>> InvokeActionWithAuthorizationFilters(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IAuthorizationFilter> filters, Func<Task<HttpResponseMessage>> innerAction)    {        filters = filters.Reverse<IAuthorizationFilter>();        return filters.Aggregate<IAuthorizationFilter, Func<Task<HttpResponseMessage>>>(innerAction, (continuation, filter) => () => filter.ExecuteAuthorizationFilterAsync(actionContext, cancellationToken, continuation));   }

 

代碼1-8

    internal static Func<Task<HttpResponseMessage>> InvokeActionWithActionFilters(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IActionFilter> filters, Func<Task<HttpResponseMessage>> innerAction)    {        filters = filters.Reverse<IActionFilter>();        return filters.Aggregate<IActionFilter, Func<Task<HttpResponseMessage>>>(innerAction, (continuation, filter) => () => filter.ExecuteActionFilterAsync(actionContext, cancellationToken, continuation));    }

 

 

這裡我們先看代碼1-5表示了過濾器執行的所有過程,突然的看起來這1-5代碼的可讀性太低了,可能是跟我水平的關係,我看起來很是吃力也比較煩躁,不過這個爛骨頭也要啃阿,放過去可能就少學會一點東西。

首先我們看到代碼1-5中調用了InvokeActionWithExceptionFilters()方法,也就是代碼1-6,那我們就看看這個InvokeActionWithExceptionFilters()方法,在個InvokeActionWithExceptionFilters()方法中有四個參數,第一個參數是Task<HttpResponseMessage>類型的,這裡打住不往下看了,回到代碼1-5中調用個InvokeActionWithExceptionFilters()方法的時候,我們看代碼的最後部分依次往前推,最後發現

代碼1-9

InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, () => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {            this._modelState = actionContext.ModelState;            return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();        }, new CancellationToken(), false))(),

發現代碼1-9的部分就是InvokeActionWithExceptionFilters()方法的參數,我們看命名也都知道InvokeActionWithExceptionFilters()方法執行的是異常過濾器的內容,第一個參數類型也說過了是Task<HttpResponseMessage>說明在這之前操作已經處理完成了不管是成功了還是有異常了咱先不管,反正代碼1-9最後產生返回的就是Task<HttpResponseMessage>類型的執行個體,那我們就來拆開代碼1-9.

從代碼1-9中可以看到首先調用的是代碼1-7的內容也就是調用了InvokeActionWithAuthorizationFilters()方法,我們看一下代碼1-7.

首先代碼1-7中的方法有四個參數(HttpActionContext actionContext, CancellationToken cancellationToken, IEnumerable<IAuthorizationFilter> filters, Func<Task<HttpResponseMessage>> innerAction),第一個控制器方法執行內容對象,跟HttpControllerContext性質都是一樣的,不說這個,第二個CancellationToken用於並行開發在並行的任務中,可以把這個類型想象成一個鉤子,你可以設定這個鉤子的狀態和行為,在任務中遇到你的鉤子會根據你的鉤子做一些操作可以是繼續任務可以是終止任務額外再執行一些其他動作(不知道理解的對不對沒深入過,有誤的話望大家指正謝謝),至於第三個參數,就是授權過濾器集合類型了,在上面說到的FilterGrouping類型中的AuthorizationFilters屬性就是用在這裡,第四個參數就比較重要了,這是一個返回Task<HttpResponseMessage>類型的委託,現在我們把代碼1-9也就是調用了InvokeActionWithAuthorizationFilters()方法的代碼中的第四個參數剝出來,然後再看下面的代碼。

代碼1-10

InvokeActionWithAuthorizationFilters(actionContext, cancellationToken, authorizationFilters, )()

從上面說的也知道現在的代碼1-10的部分只是返回一個Task<HttpResponseMessage>類型的執行個體作為代碼1-6的第一個參數,按照這樣的思路我們看一下剝離出來的第四個參數。

代碼1-11

() => actionDescriptor.ActionBinding.ExecuteBindingAsync(actionContext, cancellationToken).Then<HttpResponseMessage>(delegate {            this._modelState = actionContext.ModelState;            return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();        }, new CancellationToken(), false)

在代碼1-11裡主要會先調用actionDescriptor的ActionBinding屬性下的ExecuteBindingAsync()方法,這裡的方法就是Model綁定Model驗證所在的地方了這個後面的篇幅會講,有的朋友會發現ExecuteBindingAsync()方法返回的是Task類型,跟上面所說的所需參數的類型是Fun<Task<HttpResponseMessage>>,而這裡明顯就是Fun<Task>,是不符合的,而且按照邏輯上說也不符合阿,在授權過濾器執行完畢後應該是行為過濾器的執行阿,這裡就涉及到了一個Task的擴充方法調用,就是Then<>()方法了。

代碼1-12

internal static Task<TOuterResult> Then<TOuterResult>(this Task task, Func<Task<TOuterResult>> continuation, CancellationToken cancellationToken = new CancellationToken(), bool runSynchronously = false){    return task.ThenImpl<Task, TOuterResult>(t => continuation(), cancellationToken, runSynchronously);}

用有擴充方法的類型是私人的結構類型,這裡就不往下深入了,就在通過這裡將Task轉換成Task<HttpResponseMessage>類型的。最後我們在拆一下把這個匿名委託從代碼1-11裡面剝出來。

代碼1-13

delegate {            this._modelState = actionContext.ModelState;            return InvokeActionWithActionFilters(actionContext, cancellationToken, actionFilters, () => controllerServices.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken))();        }

看到這裡有actionContext.ModelState屬性值表示Model驗證的結果值,而這個this._modelState的this就是當前的ApiController,_modelState欄位對應的是ApiController類型中的ModelState值,在這之後調用最後的1-8代碼,在上面的1-13中我們可以看到最後是由什麼對象去執行最後的操作的,這個一系列的過程後面篇幅會講解到。

在這些所有都執行完畢了之後才會執行到代碼1-6,最後就是形成最後的代碼1-5那樣。

 

 

金源

出處:http://www.cnblogs.com/jin-yuan/

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.