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     }
View Code

    可以看到,这个类型定义的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         }
View Code

    这段代码遍历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         }
View Code

    实现各个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     }
View Code

  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     }
View Code

 

  接下来看一看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         }
View Code

  最后返回列表传递给FilterInfo类型,FilterInfo内部处理见上面.

ASP.NET MVC5学习笔记之Filter提供体系,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。