在上一篇中咱们讲到控制器的执行过程系列,这个系列要搁置一段时间了,由于在控制器执行的过程当中包含的信息都是要单独的用一个系列来描述的,就现在天的这个篇幅就是在上面内容以后所看到的一个知识要点之一。api
下面就来说解一下在ASP.NET Web API框架中过滤器的建立、执行过程。浏览器
过滤器所在的位置服务器
图1框架
图1所示的就是控制器执行过程很粗略的表示。异步
经过上一篇内容咱们了解到控制器方法选择器最后返回的并非控制器方法,而是对于控制器方法描述的类型HttpActionDescriptor,HttpActionDescriptor包含了控制器方法的一切信息,今天要讲的就是HttpActionDescriptor对象中生成的过滤器管道执行的这么一个顺序,固然其中就已经包含了建立的时候。ide
在介绍HttpActionDescriptor类型生成过滤器管道以前,咱们先来对着其中会涉及到的一些类型进行一个基础的了解。函数
基础类型一览ui
FilterInfo 过滤器对象封装信息(System.Web.Http.Filters)this
示例代码1-1spa
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/
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面