Filter与FilterProvider之间的关系

根据用途和执行时机的不同,MVC主要分为以下5种类型的过虑器:AuthenticationFilter、AuthorizationFilter、ActionFilter、ExceptionFilter、ResultFilter。下面我们来看一个IFilter接口,如下所示:

  1. public class Filter
  2. {
  3. public const int DefaultOrder = -;
  4.  
  5. public Filter(object instance, FilterScope scope, int? order)
  6. {
  7. //省略
  8. if (order == null)
  9. {
  10. IMvcFilter mvcFilter = instance as IMvcFilter;
  11. if (mvcFilter != null)
  12. {
  13. order = mvcFilter.Order;
  14. }
  15. }
  16.  
  17. Instance = instance;
  18. Order = order ?? DefaultOrder;
  19. Scope = scope;
  20. }
  21.  
  22. public object Instance { get; protected set; }
  23.  
  24. public int Order { get; protected set; }
  25.  
  26. public FilterScope Scope { get; protected set; }
  27. }
  28.  
  29. public enum FilterScope
  30. {
  31. First = ,
  32. Global = ,
  33. Controller = ,
  34. Action = ,
  35. Last = ,
  36. }

一个Filter对象就是对一个过滤器的封装,它将过滤器对象封装在Instance属性中。表示排序的Order属性,默认为-1,值越小越优先。还有一个表示范围的Scope属性,它是一个枚举类型。其中Global、Controller、Action分别表示应用到整个应用范围内、某个Controller上、某个Action上,另两个First、Last表明是应用的第一个还是最后一个。如果前面的Order属性的值一样大,那么相同的Order的Filter则会按照Scope属性值越小越优先。

所有的Filter均是通过FilterProvider来提供的,该接口的定义如下所示:

  1. public interface IFilterProvider
  2. {
  3. IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  4. }

该接口只定义了一个GetFilters方法,用于获取某个Action方法上所有的Filter。它具有两个参数ControllerContext表示当前的Controller的上下文,ActionDescriptor描述目标Action方法。

用于提供Filter的FilterProvider是通过静态类型FilterProviders来注册的。如下所示:

  1. public static class FilterProviders
  2. {
  3. static FilterProviders()
  4. {
  5. Providers = new FilterProviderCollection();
  6. Providers.Add(GlobalFilters.Filters);
  7. Providers.Add(new FilterAttributeFilterProvider());
  8. Providers.Add(new ControllerInstanceFilterProvider());
  9. }
  10.  
  11. public static FilterProviderCollection Providers { get; private set; }
  12. }

从上我们看到MVC提供了三种原生的FilterProvider,它们分别是:GlobalFilters.Filters用于提供全局、FilterAttributeFilterProvider用于提供Controller类和Action方法上、ControllerInstanceFilterProvider用于提供Controller这个别特的过滤器。下面来看FilterProvider是如何提供Filter的。

  1. 1FilterAttributeFilterProvider

我们通常是将过滤器定义成特性的方式标注到某个Controller或Action上。这样的过滤器特性一般以FilterAttribute作为基类。它实现了IMvcFilter接口,IMvcFilter只有两个只读属性成员Order和AllowMultiple。在上面的Filter类中我们看到如果实例可以转换为IMvcFilter接口,那么则将IMvcFilter的Order值赋给Filter的Order属性。我们在某个Action方法或Controller上标记某个特性时,可以在上面指定Order值,如[Authorize(Order =3)]。如下所示:

  1. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
  2. public abstract class FilterAttribute : Attribute, IMvcFilter
  3. {
  4. private static readonly ConcurrentDictionary<Type, bool> _multiuseAttributeCache = new ConcurrentDictionary<Type, bool>();
  5. private int _order = Filter.DefaultOrder;
  6.  
  7. public bool AllowMultiple
  8. {
  9. get { return AllowsMultiple(GetType()); }
  10. }
  11.  
  12. public int Order
  13. {
  14. get { return _order; }
  15. set
  16. {
  17. if (value < Filter.DefaultOrder)
  18. {
  19. throw new ArgumentOutOfRangeException("value", MvcResources.FilterAttribute_OrderOutOfRange);
  20. }
  21. _order = value;
  22. }
  23. }
  24.  
  25. private static bool AllowsMultiple(Type attributeType)
  26. {
  27. return _multiuseAttributeCache.GetOrAdd(
  28. attributeType,
  29. type => type.GetCustomAttributes(typeof(AttributeUsageAttribute), true)
  30. .Cast<AttributeUsageAttribute>()
  31. .First()
  32. .AllowMultiple);
  33. }
  34. }
  35.  
  36. public interface IMvcFilter
  37. {
  38. bool AllowMultiple { get; }
  39. int Order { get; }
  40. }
  1. 从应用在FilterAttributeAttributeUsageAttribute特性上我们可以看到此特性可以应用到类和方法上,AllowMultiple默认为false,不允许多个。

获取Controller类和Action方法上的特性是通过FilterAttributeFilterProvider来提供的。如下所示:

  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. }

从上面的静态类FilterProviders中,我们可以看到MVC默认注册的是FilterAttributeFilterProvider的无参构造函数,然后它传一个true调用有参构造函数,该_cacheAttributeInstances字段表示是否缓存获取到的Filter。因为是通过反射获取Controller和Action上的Filter,所以MVC默认缓存获取到的Filter,提高性能。

该类实现的GetFilters方法,它会先后调用GetControllerAttributes方法和GetActionAttributes方法,分别获取Cotroller和Action上Filter。分别用于描述 Controller和Action的ControllerDescriptor和ActionDescriptor类实现了ICustomAttributeProvider接口。我们可以调用相应的方法获取应用在对应的Controller类型或Action方法上包括FilterAttribute在内的所有特性。如下所示:

  1. public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
  2. {
  3. public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
  4. {
  5. if (attributeType == null)
  6. {
  7. throw new ArgumentNullException("attributeType");
  8. }
  9.  
  10. return (object[])Array.CreateInstance(attributeType, );
  11. }
  12.  
  13. public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
  14. {
  15. return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
  16. }
  17. }
  18.  
  19. public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
  20. {
  21. public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
  22. {
  23. if (attributeType == null)
  24. {
  25. throw new ArgumentNullException("attributeType");
  26. }
  27.  
  28. return (object[])Array.CreateInstance(attributeType, );
  29. }
  30.  
  31. public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
  32. {
  33. return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
  34. }
  35. }

2、ControllerInstanceFilterProvider

Controller本身就是一个过滤器,如下所示:

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略
  4. }

对于Controller这个特殊的过滤器,其对应的FilterProvider类型,如下所示:

  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. }

它的GetFilter方法跟据ControllerContext获取对应的Controller对象,并以此创建对应的Filter。

 3、GlobalFilters.Filters

全局过滤器,它不是显示的应用于某个Controller、某个Action上,而是默认应用到整个程序的所有Controller类型的Action方法上。如下所示:

  1. public static class GlobalFilters
  2. {
  3. static GlobalFilters()
  4. {
  5. Filters = new GlobalFilterCollection();
  6. }
  7.  
  8. public static GlobalFilterCollection Filters { get; private set; }
  9. }
  1. GlobalFilters就只有一个属性GlobalFilterCollectionGlobalFilterCollection它是一个元素类型为Filter的集合,它显示地实现了IFilterProvider接口的GetFilters方法,返回的就是它自己,如下所示:
  1. public sealed class GlobalFilterCollection : IEnumerable<Filter>, IFilterProvider
  2. {
  3. private List<Filter> _filters = new List<Filter>();
  4.  
  5. public void Add(object filter)
  6. {
  7. AddInternal(filter, order: null);
  8. }
  9.  
  10. public void Add(object filter, int order)
  11. {
  12. AddInternal(filter, order);
  13. }
  14.  
  15. private void AddInternal(object filter, int? order)
  16. {
  17. ValidateFilterInstance(filter);
  18. _filters.Add(new Filter(filter, FilterScope.Global, order));
  19. }
  20.  
  21. IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
  22. {
  23. return this;
  24. }
  25.   //省略
  26. }

到目前为止,MVC默认提供的3种原生的FilterProvider以及各自采用的Filter提供机制介绍完毕。MVC的5种过滤器最终被封装成相应的Filter对象,但是它们的执行时机和方式是不同的,所以在执行之前需要根据被封装的过滤器类型对所有的Filter进行分组。就是当ControllerActionInvoker被调用时,它会利用静态类型FilterProviders得到所有注册的IFilterProvider类型,然后利用当前的ControllerContext和ActionDescriptor对象得到Filter,然后根据其Instance属性表示的过滤器类型,将它分组,最终得到一个具有如下所示FilterInfo类型的对象。

  1. public class FilterInfo
  2. {
  3. public IList<IActionFilter> ActionFilters { get; }
  4.  
  5. public IList<IAuthenticationFilter> AuthenticationFilters { get; }
  6.  
  7. public IList<IAuthorizationFilter> AuthorizationFilters { get; }
  8.  
  9. public IList<IExceptionFilter> ExceptionFilters { get; }
  10.  
  11. public IList<IResultFilter> ResultFilters { get; }
  12. }

简说MVC Filter的更多相关文章

  1. MVC Filter 实现方式和作用范围控制

    Asp.Net MVC Filter 实现方式和作用范围控制 MVC中的Filte 简单又优雅的实现了AOP ,在日志,权限,缓存和异常处理等方面用的比较多.但本文不是讨论Filter这些功能点,而是 ...

  2. 学习之-ASP.NET MVC Filter

    MVC Filter 是典型的AOP应用,对MVC框架处理客户端请求注入额外的一些逻辑,如日志记录.缓存处理.异常处理和权限验证,性能检测(横切关注点),而这些逻辑通常与主要业务无关,被独立分开作为公 ...

  3. .NET MVC Filter异常处理

    MVC程序中自带的HandleErrorAttribute,来处理异常,不在显示黄页.前提是在web.config 中 system.web中关闭customerError选项. 但是很多情况下调试异 ...

  4. IoC容器Autofac(5) - Autofac在Asp.net MVC Filter中的应用

    Autofac结合EF在MVC中的使用,上一篇IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源码)已经介绍了.但是只是MVC中Co ...

  5. Asp.Net MVC Filter 实现方式和作用范围控制

    MVC中的Filte 简单又优雅的实现了AOP ,在日志,权限,缓存和异常处理等方面用的比较多.但本文不是讨论Filter这些功能点,而是总结Filter实现的方式.说实现也不太准确,也就是它的呈现方 ...

  6. MVC Filter自定义验证(拦截)

    namespace QS.Web.Extensions { /// <summary> /// 验证session.权限 状态 /// </summary> [Attribut ...

  7. ASP.NET MVC Filter的思考

    思考了一下AOP的具体实现,后来想到ASP.NET MVC过滤器其实就是AOP的一种,于是从Filter下手研究AOP. 暂时先考虑AuthorizationFilter,ActionFilter,R ...

  8. MVC Filter

    一.Filter在MVC生命周期中的位置 1.IIS中传递请求到程序2.MVC根据Routing来选择由哪个Controller/Action来处理3.Controller调用Model(业务逻辑)来 ...

  9. Asp.net Mvc (Filter及其执行顺序)

    应用于Action的Filter 在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能判断登录与否或用户权限,决策输出缓存,防盗链,防蜘蛛,本地化设置,实现动态Actionfi ...

随机推荐

  1. redis 数据持久化 aof方式

    redis持久化-Append-only file(缩写aof)的方式 本质:把用户执行的每个  ”写“ 指令(增加.修改.删除)都备份到文件中,还原数据的时候就是执行具体写指令. 打开redis的运 ...

  2. struts2上传单个文件

    项目目录: struts.xml配置: <constant name="struts.enable.DynamicMethodInvocation" value=" ...

  3. 19.Class的基本语法

    1.简介 JavaScript 语言中,生成实例对象的传统方法是通过构造函数. function Point(x, y) { this.x = x; this.y = y; } Point.proto ...

  4. 向div添加圆角边框

    初级参数:border-radius: 4px;中级参数:border-radius: 4px 6px 6px 4px;终极参数:border-radius: 5px 5px 3px 2px / 5p ...

  5. java 位运算符,逻辑运算符

    逻辑运算符;布尔值时使用 a=true;b=false &: 逻辑或   例:a & b=false; |: 逻辑与   例:a | b=true; !:逻辑非    例:!a=fal ...

  6. 关系型数据库MySQL多实例

    简介 MySQL数据库是一个中小型关系型数据库管理系统,软件开发者为瑞典MySQL AB公司.在2008年1月16号被Sun公司收购后Sun公司又被oracle公司收购.目前MySQL被广泛地应用在I ...

  7. 关于rails中 rake db:create 失败的问题

    提示信息: rake aborted!Could not find a JavaScript runtime. See https://github.com/sstephenson/execjsfor ...

  8. TP2.1 加载扩展配置文件参数

    维护老项目真的恶心!!!!!!!!! TP3.2好像有这样一个配置参数,可以设置有那些扩展配置文件,系统会自动加载. 方法一: 'LOAD_EXT_CONFIG' => 'user,db',// ...

  9. ubuntu下搭建ecshop

        最近在看ecmobile的开源项目,可以从http://www.ecmobile.cn/agreement.html下载源码或者从github上下载源码https://github.com/G ...

  10. Java新人拿到一台新的电脑需要装配什么

    适用对象:新手 装备:win10 一.IDEA 插件等 说明 其他 TranslationPlugin 翻译插件 GitHub地址 MyBatis-Generator 自动生成Mybatis文件 Al ...