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)