zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Python 爬虫js加密破解(一) 爬取今日头条as cp 算法 解密
    Python 爬虫实例(2)—— 爬取今日头条
    Python 爬虫实例(1)—— 爬取百度图片
    python 操作redis之——HyperLogLog (八)
    python 操作redis之——有序集合(sorted set) (七)
    Python操作redis系列之 列表(list) (五)
    Python操作redis系列以 哈希(Hash)命令详解(四)
    Python操作redis字符串(String)详解 (三)
    How to Install MySQL on CentOS 7
    Linux SSH远程文件/目录 传输
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2268076.html
Copyright © 2011-2022 走看看