zoukankan      html  css  js  c++  java
  • ASP.NET Web API 过滤器创建、执行过程(二)

    ASP.NET Web API 过滤器创建、执行过程(二)

    前言

    前面一篇中讲解了过滤器执行之前的创建,通过实现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/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

  • 相关阅读:
    浅谈REST[转]
    linuxyum
    XAMPPAccess denied for user 'root'@'localhost' (using password:YES)....& can't connect to localhost...
    ps aux详细解释【转】
    TOP 10:值得关注的十家云计算公司【转】
    isnull在数据库查询中的应用
    正则匹配代码
    推荐一款生成SQL插入语句的软件
    网页自动刷新
    执行SQL脚本语句判断是否已经存在
  • 原文地址:https://www.cnblogs.com/jin-yuan/p/3971369.html
Copyright © 2011-2022 走看看