1. 程式人生 > >ASP.NET Web API 過濾器創建、執行過程(二)

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

reading mar model驗證 type() 方法的參數 public 所有 ring attr

前言

前面一篇中講解了過濾器執行之前的創建,通過實現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 過濾器創建、執行過程(二)