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

来源:未知 责任编辑:责任编辑 发表时间:2013-08-22 04:53 点击:

在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

  

 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

  

 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

  

 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

  

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

  

  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

  

 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);

  

 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了。

 

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

谢谢。

  作者 十一月的雨

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    用户名: 验证码:点击我更换图片
    最新评论 更多>>

    推荐热点

    • 浅析.NET下XML数据访问新机制
    • asp.net 面试+笔试题目第1/2页
    • C# 邮件地址是否合法的验证
    • asp.net 设置GridView的选中行的实现代码
    • C#高级编程:数据库连接[1]
    • 经典C++程序1
    • IIS 自动回收导致后台定时器失效的问题解决
    • ASP.NET&#160;GridView列表代码示例
    • Asp.net MVC源码分析--Action Filter的链式调用
    网站首页 - 友情链接 - 网站地图 - TAG标签 - RSS订阅 - 内容搜索
    Copyright © 2008-2015 计算机技术学习交流网. 版权所有

    豫ICP备11007008号-1