ASP.NET MVC5学习笔记之Filter提供体系
前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的。
一.Filter集合
在ControlerActionInvoker的InvokeAction方法中,只有一行代码FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor), 把收集的Filter信息放到了FilterInfo中,我们来看看
访类型的定义:
1 public class FilterInfo 2 { 3 4 public FilterInfo(); 5 6 public FilterInfo(IEnumerable<Filter> filters); 7 8 9 public IList<IActionFilter> ActionFilters { get; } 10 public IList<System.Web.Mvc.Filters.IAuthenticationFilter> AuthenticationFilters { get; } 11 12 public IList<IAuthorizationFilter> AuthorizationFilters { get; } 13 14 public IList<IExceptionFilter> ExceptionFilters { get; } 15 16 public IList<IResultFilter> ResultFilters { get; } 17 }
可以看到,这个类型定义的5种系统Filter集合信息. 来看看它带参的构造函数:
public FilterInfo(IEnumerable<Filter> filters) { // Determine the override scope for each filter type and cache the filters list. OverrideFilterInfo processed = ProcessOverrideFilters(filters); // Split the cached filters list based on filter type and override scope. SplitFilters(processed); }
可以看到分两步:
1 .ProcessOverrideFilters处理前面的提到IOverrideFilter接口
2. 从列表中把Filter分离到各自的Filter接口集合
ProcessOverrideFilters方法的代码如下:
1 private static OverrideFilterInfo ProcessOverrideFilters(IEnumerable<Filter> filters) 2 { 3 OverrideFilterInfo result = new OverrideFilterInfo 4 { 5 ActionOverrideScope = FilterScope.First, 6 AuthenticationOverrideScope = FilterScope.First, 7 AuthorizationOverrideScope = FilterScope.First, 8 ExceptionOverrideScope = FilterScope.First, 9 ResultOverrideScope = FilterScope.First, 10 Filters = new List<Filter>() 11 }; 12 13 // Evaluate the 'filters' enumerable only once since the operation can be quite expensive. 14 foreach (Filter filter in filters) 15 { 16 if (filter == null) 17 { 18 continue; 19 } 20 IOverrideFilter overrideFilter = filter.Instance as IOverrideFilter; 21 22 if (overrideFilter != null) 23 { 24 if (overrideFilter.FiltersToOverride == typeof(IActionFilter) 25 && filter.Scope >= result.ActionOverrideScope) 26 { 27 result.ActionOverrideScope = filter.Scope; 28 } 29 else if (overrideFilter.FiltersToOverride == typeof(IAuthenticationFilter) 30 && filter.Scope >= result.AuthenticationOverrideScope) 31 { 32 result.AuthenticationOverrideScope = filter.Scope; 33 } 34 else if (overrideFilter.FiltersToOverride == typeof(IAuthorizationFilter) 35 && filter.Scope >= result.AuthorizationOverrideScope) 36 { 37 result.AuthorizationOverrideScope = filter.Scope; 38 } 39 else if (overrideFilter.FiltersToOverride == typeof(IExceptionFilter) 40 && filter.Scope >= result.ExceptionOverrideScope) 41 { 42 result.ExceptionOverrideScope = filter.Scope; 43 } 44 else if (overrideFilter.FiltersToOverride == typeof(IResultFilter) 45 && filter.Scope >= result.ResultOverrideScope) 46 { 47 result.ResultOverrideScope = filter.Scope; 48 } 49 } 50 51 // Cache filters to avoid having to enumerate it again (expensive). Do so here to avoid an extra loop. 52 result.Filters.Add(filter); 53 } 54 55 return result; 56 }
这段代码遍历Filter列表,记录实现了IOverrideFilter的Filter的最高OverrideScope, 在下面的SplitFilters处理中,少于OverrideScope的Filter将不会添加到最终的集合中
SplitFilters方法代码如下:
1 private void SplitFilters(OverrideFilterInfo info) 2 { 3 Contract.Assert(info.Filters != null); 4 5 foreach (Filter filter in info.Filters) 6 { 7 Contract.Assert(filter != null); 8 9 IActionFilter actionFilter = filter.Instance as IActionFilter; 10 11 if (actionFilter != null && filter.Scope >= info.ActionOverrideScope) 12 { 13 _actionFilters.Add(actionFilter); 14 } 15 16 IAuthenticationFilter authenticationFilter = filter.Instance as IAuthenticationFilter; 17 18 if (authenticationFilter != null && filter.Scope >= info.AuthenticationOverrideScope) 19 { 20 _authenticationFilters.Add(authenticationFilter); 21 } 22 23 IAuthorizationFilter authorizationFilter = filter.Instance as IAuthorizationFilter; 24 25 if (authorizationFilter != null && filter.Scope >= info.AuthorizationOverrideScope) 26 { 27 _authorizationFilters.Add(authorizationFilter); 28 } 29 30 IExceptionFilter exceptionFilter = filter.Instance as IExceptionFilter; 31 32 if (exceptionFilter != null && filter.Scope >= info.ExceptionOverrideScope) 33 { 34 _exceptionFilters.Add(exceptionFilter); 35 } 36 37 IResultFilter resultFilter = filter.Instance as IResultFilter; 38 39 if (resultFilter != null && filter.Scope >= info.ResultOverrideScope) 40 { 41 _resultFilters.Add(resultFilter); 42 } 43 } 44 }
实现各个Filter的分离,代码很简单,不再说明。
二. Filter收集
在ASP.NET MVC5中最终通过FilterProviders.Providers.GetFilters方法得到所有的Filter列表,我们先来看看FilterProviders这个类型,定义如下:
public static class FilterProviders { static FilterProviders() { Providers = new FilterProviderCollection(); Providers.Add(GlobalFilters.Filters); Providers.Add(new FilterAttributeFilterProvider()); Providers.Add(new ControllerInstanceFilterProvider()); } public static FilterProviderCollection Providers { get; private set; } }
从中我们可以看到系统定义了三个FilterProvider,它们都实现了IFilterProvider接口,该接口定义如下:
public interface IFilterProvider { IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }
现在来看看这几个Provider:
a. GlobalFilters.Filters
这个故名思义是收集全局范围运行的Filter,代码如下:
public static class GlobalFilters { static GlobalFilters() { Filters = new GlobalFilterCollection(); } public static GlobalFilterCollection Filters { get; private set; } }
比如通常的项目模板在App_Start的FilterConfig中有如下代码,添加全局出错处理
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); }
b. FilterAttributeFilterProvider
该类型是帮助收集应用在Controller和Action上的Filter,代码如下:
1 public class FilterAttributeFilterProvider : IFilterProvider 2 { 3 private readonly bool _cacheAttributeInstances; 4 5 public FilterAttributeFilterProvider() 6 : this(true) 7 { 8 } 9 10 public FilterAttributeFilterProvider(bool cacheAttributeInstances) 11 { 12 _cacheAttributeInstances = cacheAttributeInstances; 13 } 14 15 protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 16 { 17 return actionDescriptor.GetFilterAttributes(_cacheAttributeInstances); 18 } 19 20 protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 21 { 22 return actionDescriptor.ControllerDescriptor.GetFilterAttributes(_cacheAttributeInstances); 23 } 24 25 public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 26 { 27 // Results are low in number in the common case so use yield return to avoid creating intermediate collections or nested enumerables 28 if (controllerContext.Controller != null) 29 { 30 foreach (FilterAttribute attr in GetControllerAttributes(controllerContext, actionDescriptor)) 31 { 32 yield return new Filter(attr, FilterScope.Controller, order: null); 33 } 34 foreach (FilterAttribute attr in GetActionAttributes(controllerContext, actionDescriptor)) 35 { 36 yield return new Filter(attr, FilterScope.Action, order: null); 37 } 38 } 39 } 40 }
c. ControllerInstanceFilterProvider
controller本身也实现了一些Filter接口,通过该Provider加入,代码如下:
1 public class ControllerInstanceFilterProvider : IFilterProvider 2 { 3 public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 4 { 5 if (controllerContext.Controller != null) 6 { 7 // Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first 8 yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue); 9 } 10 } 11 }
接下来看一看FilterProviderCollection的GetFilters实现:
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { //省略检查代码 IFilterProvider[] providers = CombinedItems; List<Filter> filters = new List<Filter>(); for (int i = 0; i < providers.Length; i++) { IFilterProvider provider = providers[i]; foreach (Filter filter in provider.GetFilters(controllerContext, actionDescriptor)) { filters.Add(filter); } } filters.Sort(_filterComparer); if (filters.Count > 1) { RemoveDuplicates(filters); } return filters; }
从中我们可以看到,主要分为三步:
1. 通过FilterProviders 把Filter收集到一个Filter列表
2. 对列表进行排序,排序规则是根据Order和Scope
3. 列表去重
FilterComparer是Filter排序比较器,代码如下, 从中我们可以看到Order 和 Scope是怎么影响排序顺序
1 private class FilterComparer : IComparer<Filter> 2 { 3 public int Compare(Filter x, Filter y) 4 { 5 // Nulls always have to be less than non-nulls 6 if (x == null && y == null) 7 { 8 return 0; 9 } 10 if (x == null) 11 { 12 return -1; 13 } 14 if (y == null) 15 { 16 return 1; 17 } 18 19 // Sort first by order... 20 21 if (x.Order < y.Order) 22 { 23 return -1; 24 } 25 if (x.Order > y.Order) 26 { 27 return 1; 28 } 29 30 // ...then by scope 31 32 if (x.Scope < y.Scope) 33 { 34 return -1; 35 } 36 if (x.Scope > y.Scope) 37 { 38 return 1; 39 } 40 41 return 0; 42 } 43 }
最后返回列表传递给FilterInfo类型,FilterInfo内部处理见上面.