• Asp.net MVC源码分析Filter种类以及调用优先级


    在Asp.net MVC 框架中一共有四种型的Filter,它们分别是

    1.IActionFilter
    2.IAuthorizationFilter
    3.IExceptionFilter
    4.IResultFilter

    别外再加上一个GlobalFilters.Filters全局的,看起来挺多但是基本上这些Filter都与Action的调用有关,

    让我沿着Mvc3.0源码一一找出它们的线索。

    首先让我们看FilterProviders.cs,这是一个全局的系统默认FilterFilter provider,当然们也可以向里面加自定义的provider.

    FilterProviders.cs

    View Code
     1 namespace System.Web.Mvc {
    2 public static class FilterProviders {
    3 static FilterProviders() {
    4 Providers = new FilterProviderCollection();
    5 Providers.Add(GlobalFilters.Filters);
    6 Providers.Add(new FilterAttributeFilterProvider());
    7 Providers.Add(new ControllerInstanceFilterProvider());
    8 }
    9
    10 public static FilterProviderCollection Providers {
    11 get;
    12 private set;
    13 }
    14 }
    15 }

    这里面最为重要的是FilterAttributeFilterProvider,它提供了找出Action所有在元数据中Filter的功能方法。

    FilterAttributeFilterProvider.cs

    View Code
     1   public class FilterAttributeFilterProvider : IFilterProvider {
    2 private readonly bool _cacheAttributeInstances;
    3
    4 public FilterAttributeFilterProvider()
    5 : this(true) {
    6 }
    7
    8 public FilterAttributeFilterProvider(bool cacheAttributeInstances) {
    9 _cacheAttributeInstances = cacheAttributeInstances;
    10 }
    11
    12 protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
    13 return actionDescriptor.GetFilterAttributes(_cacheAttributeInstances);
    14 }
    15
    16 protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
    17 return actionDescriptor.ControllerDescriptor.GetFilterAttributes(_cacheAttributeInstances);
    18 }
    19
    20 public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
    21 ControllerBase controller = controllerContext.Controller;
    22 if (controller == null) {
    23 return Enumerable.Empty<Filter>();
    24 }
    25
    26 var typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)
    27 .Select(attr => new Filter(attr, FilterScope.Controller, null));
    28 var methodFilters = GetActionAttributes(controllerContext, actionDescriptor)
    29 .Select(attr => new Filter(attr, FilterScope.Action, null));
    30
    31 return typeFilters.Concat(methodFilters).ToList();
    32 }
    33 }

    -------------------------------------------------------------------------------------------------
    接下来我们看Mvm框架是如何获取和使用这些Filter的:

    ControllerActionInvoker.cs

    View Code
     1  public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {
    2 if (controllerContext == null) {
    3 throw new ArgumentNullException("controllerContext");
    4 }
    5 if (String.IsNullOrEmpty(actionName)) {
    6 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
    7 }
    8
    9 ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
    10 ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
    11 if (actionDescriptor != null) {
    12 FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
    13
    14 try {
    15 AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
    16 if (authContext.Result != null) {
    17 // the auth filter signaled that we should let it short-circuit the request
    18 InvokeActionResult(controllerContext, authContext.Result);
    19 }
    20 else {
    21 if (controllerContext.Controller.ValidateRequest) {
    22 ValidateRequest(controllerContext);
    23 }
    24
    25 IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
    26 ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
    27 InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
    28 }
    29 }
    30 catch (ThreadAbortException) {
    31 // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
    32 // the filters don't see this as an error.
    33 throw;
    34 }
    35 catch (Exception ex) {
    36 // something blew up, so execute the exception filters
    37 ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
    38 if (!exceptionContext.ExceptionHandled) {
    39 throw;
    40 }
    41 InvokeActionResult(controllerContext, exceptionContext.Result);
    42 }
    43
    44 return true;
    45 }
    46
    47 // notify controller that no method matched
    48 return false;
    49 }

    在ControllerActionInvoker.InvokeAction 方法中我们看到一句

    FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);//这是得到Action和 Controller所有Filter的方法实现.

    ControllerActionInvoker.cs

    View Code
    1 protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
    2 return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
    3 }
    4
    5
    6 private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = (cc, ad) => FilterProviders.Providers.GetFilters(cc, ad);

    这时我们看到了GetFilters最终调用了, FilterProviders.Providers.GetFilters 来得到Filter,那其中

    又用到了FilterAttributeFilterProvider 来获取在元数据的Attribute中Action 和 Controller 的Filter.

    ------------------------------------------------------------------------------------------------

    而我们如何来控制Filter的调用顺序呢?

    FilterProviderCollection.cs

    View Code
      1 namespace System.Web.Mvc {
    2 using System.Collections.Generic;
    3 using System.Collections.ObjectModel;
    4 using System.Linq;
    5
    6 public class FilterProviderCollection : Collection<IFilterProvider> {
    7
    8 private static FilterComparer _filterComparer = new FilterComparer();
    9 private IResolver<IEnumerable<IFilterProvider>> _serviceResolver;
    10
    11 public FilterProviderCollection() {
    12 _serviceResolver = new MultiServiceResolver<IFilterProvider>(() => Items);
    13 }
    14
    15 public FilterProviderCollection(IList<IFilterProvider> providers)
    16 : base(providers) {
    17 _serviceResolver = new MultiServiceResolver<IFilterProvider>(() => Items);
    18 }
    19
    20 internal FilterProviderCollection(IResolver<IEnumerable<IFilterProvider>> serviceResolver, params IFilterProvider[] providers)
    21 : base(providers) {
    22 _serviceResolver = serviceResolver ?? new MultiServiceResolver<IFilterProvider>(
    23 () => Items
    24 );
    25 }
    26
    27 private IEnumerable<IFilterProvider> CombinedItems {
    28 get {
    29 return _serviceResolver.Current;
    30 }
    31 }
    32
    33 private static bool AllowMultiple(object filterInstance) {
    34 IMvcFilter mvcFilter = filterInstance as IMvcFilter;
    35 if (mvcFilter == null) {
    36 return true;
    37 }
    38
    39 return mvcFilter.AllowMultiple;
    40 }
    41
    42 public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
    43 if (controllerContext == null) {
    44 throw new ArgumentNullException("controllerContext");
    45 }
    46 if (actionDescriptor == null) {
    47 throw new ArgumentNullException("actionDescriptor");
    48 }
    49
    50 IEnumerable<Filter> combinedFilters =
    51 CombinedItems.SelectMany(fp => fp.GetFilters(controllerContext, actionDescriptor))
    52 .OrderBy(filter => filter, _filterComparer);
    53
    54 // Remove duplicates from the back forward
    55 return RemoveDuplicates(combinedFilters.Reverse()).Reverse();
    56 }
    57
    58 private IEnumerable<Filter> RemoveDuplicates(IEnumerable<Filter> filters) {
    59 HashSet<Type> visitedTypes = new HashSet<Type>();
    60
    61 foreach (Filter filter in filters) {
    62 object filterInstance = filter.Instance;
    63 Type filterInstanceType = filterInstance.GetType();
    64
    65 if (!visitedTypes.Contains(filterInstanceType) || AllowMultiple(filterInstance)) {
    66 yield return filter;
    67 visitedTypes.Add(filterInstanceType);
    68 }
    69 }
    70 }
    71
    72 private class FilterComparer : IComparer<Filter> {
    73 public int Compare(Filter x, Filter y) {
    74 // Nulls always have to be less than non-nulls
    75 if (x == null && y == null) {
    76 return 0;
    77 }
    78 if (x == null) {
    79 return -1;
    80 }
    81 if (y == null) {
    82 return 1;
    83 }
    84
    85 // Sort first by order...
    86
    87 if (x.Order < y.Order) {
    88 return -1;
    89 }
    90 if (x.Order > y.Order) {
    91 return 1;
    92 }
    93
    94 // ...then by scope
    95
    96 if (x.Scope < y.Scope) {
    97 return -1;
    98 }
    99 if (x.Scope > y.Scope) {
    100 return 1;
    101 }
    102
    103 return 0;
    104 }
    105 }
    106 }
    107 }

    我们看到这里_filterComparer 起到了关键的作用,它通过Filter.Order 和 Filter.Scope 来决定了调用的优先顺序

    IEnumerable<Filter> combinedFilters = 
    CombinedItems.SelectMany(fp => fp.GetFilters(controllerContext, actionDescriptor))
    .OrderBy(filter => filter, _filterComparer);

    到这里我们已经知道如果获取Filter和filter的优先级别。

    -------------------------------------------------------------------------------------------------

    同时我们还需要注意一点的是在FilterProviderCollection的构造函数中:

     _serviceResolver = new MultiServiceResolver<IFilterProvider>(() => Items);

    会把DependenyResolver中的注册的IFilterProvider 和 Items(自已的合并)。

    MultiServiceResolver.cs

    View Code
     1  internal class MultiServiceResolver<TService> : IResolver<IEnumerable<TService>> where TService : class {
    2 private IEnumerable<TService> _itemsFromService;
    3 private Func<IEnumerable<TService>> _itemsThunk;
    4 private Func<IDependencyResolver> _resolverThunk;
    5
    6 public MultiServiceResolver(Func<IEnumerable<TService>> itemsThunk) {
    7 if (itemsThunk == null) {
    8 throw new ArgumentNullException("itemsThunk");
    9 }
    10
    11 _itemsThunk = itemsThunk;
    12 _resolverThunk = () => DependencyResolver.Current;
    13 }
    14
    15 internal MultiServiceResolver(Func<IEnumerable<TService>> itemsThunk, IDependencyResolver resolver)
    16 : this(itemsThunk) {
    17 if (resolver != null) {
    18 _resolverThunk = () => resolver;
    19 }
    20 }
    21
    22 public IEnumerable<TService> Current {
    23 get {
    24 if (_itemsFromService == null) {
    25 lock (_itemsThunk) {
    26 if (_itemsFromService == null) {
    27 _itemsFromService = _resolverThunk().GetServices<TService>();
    28 }
    29 }
    30 }
    31 return _itemsFromService.Concat(_itemsThunk());
    32 }
    33 }
    34 }
    35 }

    这就为我们提供了二个注册IFilterProvider 的时点:DependencyResolver/FilterProviders.Providers,和一注册全局Filter的点GlobalFilters.Filters

    -----------------------------------------------------------------------------------------------------

    调用:

    到这里我们已经知道如果获取Filter和filter的优先级别,那么我们如何来使用这些Filter呢?

    在InvokeAction 方法中我们看到

    FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

    View Code
     1  public class FilterInfo {
    2 private List<IActionFilter> _actionFilters = new List<IActionFilter>();
    3 private List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
    4 private List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();
    5 private List<IResultFilter> _resultFilters = new List<IResultFilter>();
    6
    7 public FilterInfo() {
    8 }
    9
    10 public FilterInfo(IEnumerable<Filter> filters) {
    11 // evaluate the 'filters' enumerable only once since the operation can be quite expensive
    12 var filterInstances = filters.Select(f => f.Instance).ToList();
    13
    14 _actionFilters.AddRange(filterInstances.OfType<IActionFilter>());
    15 _authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());
    16 _exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());
    17 _resultFilters.AddRange(filterInstances.OfType<IResultFilter>());
    18 }

    这里会把得到的Filters 分类放好供MVC调用,这时我们看到了以下几句代码就是对Filter的调用。

     1 //IAuthorizationFilter调用
    2 AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor)
    3
    4 //IActionFilter调用
    5 ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
    6
    7 //IResultFilter调用
    8 InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
    9
    10 //IExceptionFilter调用
    11 ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);

    当然这些调用时会判断调用状态,如果IAuthorizationFilter没有成功,这时authContext.Result 就会有值,

    就会调用InvokeActionResult(controllerContext, authContext.Result); 而不会往下调用其它Filter了。

    以上分析如各位看客有不同意见,欢迎给砖。:)

    谢谢。

    转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html 

    本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

  • 相关阅读:
    kotlin类与对象——>对象表达式与对象声明、内联类
    kotlin类与对象——>嵌套类与内部类、枚举类
    kotlin类与对象——>数据类、密封类、泛型
    UIView的setNeedsLayout, layoutIfNeeded 和 layoutSubviews 方法之间的关系解释
    xcode 统计代码行数
    iOS 常用的一些公用方法
    如何在一个项目中去建立多个Target
    instrument linker 的使用
    25 优化技巧
    二维码生成与扫描
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2268076.html
Copyright © 2020-2023  润新知