Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把【筛子】,那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从而作出响应,这对于认证以及授权等是及其重要的,所以说过滤器应用是Web API框架中非常重要的一种实现方式,我们有必要去探讨其原理。
过滤器及其提供机制
Web API框架提供了一个请求、响应的消息处理管道,并且其框架极具扩展性,通过其扩展可以对执行的流程进行适当的干预,其扩展点就体现在以下三个方面:
public class ConfigurationFilterProvider : IFilterProvider{ // Methods public ConfigurationFilterProvider(); public IEnumerable GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor);}
我们查看其方法的实现:
public IEnumerable GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor){ if (configuration == null) { throw Error.ArgumentNull("configuration"); } return configuration.Filters;}
我们继续看看HttpConfiguration中的属性Filters具体是什么:
public HttpFilterCollection Filters{ get { return this._filters; }}
知,Filters是过滤器的集合HttpFilterCollection,我们再来详细看看此类
public class HttpFilterCollection : IEnumerable , IEnumerable{ // Fields private readonly List _filters; // Methods public HttpFilterCollection(); public void Add(IFilter filter); private void AddInternal(FilterInfo filter); public void Clear(); public bool Contains(IFilter filter); public IEnumerator GetEnumerator(); public void Remove(IFilter filter); IEnumerator IEnumerable.GetEnumerator(); // Properties public int Count { get; }}
public IEnumerable GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor){ if (configuration == null) { throw Error.ArgumentNull("configuration"); } if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } IEnumerable first = from instance in actionDescriptor.ControllerDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Controller); IEnumerable second = from instance in actionDescriptor.GetFilters() select new FilterInfo(instance, FilterScope.Action); return first.Concat (second);}
public class OneFilter :System.Web.Http.Filters.ActionFilterAttribute { } public class TwoFilter : System.Web.Http.Filters.ActionFilterAttribute { } public class ThreeFilter : System.Web.Http.Filters.ActionFilterAttribute { }
在Web API配置文件中添加一个全局过滤器:
config.Filters.Add(new ThreeFilter());
最后实现调用并查看其顺序:
[OneFilter] public class ProductController : ApiController { [TwoFilter] public IEnumerable > GetFilter() { var actionSelector = this.Configuration.Services.GetActionSelector(); var actionDesciptor = actionSelector.SelectAction(this.ControllerContext); foreach (var filterInfo in actionDesciptor.GetFilterPipeline()) { yield return new Tuple (filterInfo.Instance.GetType().Name, filterInfo.Scope); } } }
[OneFilter] public class ProductController : ApiController { public IEnumerable > GetFilter() { List list = new List (); var actionSelector = this.Configuration.Services.GetActionSelector(); var actionDesciptor = actionSelector.SelectAction(this.ControllerContext); foreach (var filterInfo in actionDesciptor.GetFilterPipeline()) { yield return new Tuple (filterInfo.Instance.GetType().Name, filterInfo.Scope); } } }
由上知,我们只是去掉了Action方法上的特性,接下来我们来再看看其结果:
至此,我们可以得出如下结论
Web API框架多次利用同一Filter在同一目标元素时,所采用唯一性Filter策略,并且Filter总是采用FilterScope最大的那一个,换言之,对于其过滤器作用域(FilterScope)的优先级是:Action-----------------> Controller ------------------>Global
那么问题来了,对于这种唯一性Filter策略是如何实现的呢?请往下看。
唯一性Filter策略实现原理
在实现唯一性Filter策略也就是在初始化过滤器的管道过程中,如下:
private Collection InitializeFilterPipeline(){ return new Collection (RemoveDuplicates((from fp in this._configuration.Services.GetFilterProviders() select fp.GetFilters(this._configuration, this)).OrderBy (f => f, FilterInfoComparer.Instance).Reverse ()).Reverse ().ToList ());}