欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-09-02 22:57:30
在介绍HttpActionDescriptor类型生成过滤器管道之前,我们先来对着其中会涉及到的一些类型进行一个基础的了解。   基础类型一览   Fi...
在介绍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("https://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委托来进行操作,便于外部的叠加器使用。