在ActionInvoker对Action的执行过程中,除了通过利用ActionDescriptor对Action方法的执行,以及之前进行的Model绑定与验证之外,还具有一个重要的工作,那就是对相关筛选器(Filter)的执行。ASP.NET MVC的筛选器是一种基于AOP(面向方面编程)的设计,我们将一些非业务的逻辑实现在相应的筛选器中,然后以一种横切(Crosscutting)的方式应用到对应的Action方法。当Action方法执行前后,这些筛选器会自动执行。ASP.NET MVC提供了四种类型的筛选器(AuthorizationFilter、ActionFilter、ResultFilter和ExceptionFilter),它们对应着相应的筛选器接口(IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter)。[本文已经同步到《How ASP.NET MVC Works?》中]
目录
一、Filter
二、FilterProvider
三、FilterAttribute与FilterAttributeFilterProvider
四、Controller与ControllerInstanceFilterProvider
五、GlobalFilterCollection
六、实例演示:验证Filter的提供机制和执行顺序
一、Filter
虽然ASP.NET MVC提供的四种类型的筛选器具有各自实现的接口,但是对于筛选器的提供体系来说所有的筛选器都通过具有如下定义的Filter类型表示。Filter的核心是Instance属性,因为它代表真正实施筛选功能的对象,该对象实现了一个或者多个基于上述四种筛选器类型的接口。
public class Filter { public const int DefaultOrder = -1; public Filter(object instance, FilterScope scope, int? order); public object Instance { get; protected set; } public int Order { get; protected set; } public FilterScope Scope { get; protected set; } } public enum FilterScope { Action = 30, Controller = 20, First = 0, Global = 10, Last = 100 }</div>
注:由于System.Web.Mvc.Filter和实现了IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter的类型均可以被称为“筛选器”,为了不至于造成混淆,在没有做明确说明的情况下,我们使用英文“Filter”和中文“筛选器”分别来表示它们。
Filter的Order和Scope属性最终决定了筛选器的执行顺序。Order属性对应数值越小,执行的优先级越高,该属性的默认值为-1(对应着Filter中定义的常量DefaultOrder)。如果两个Filter具有相同的Order属性值,那么Scope属性最终决定哪个被优先执行。Filter的Scope属性类型是一个类型为FilterScope的枚举。该枚举表示应用Filter的范围,Action和Controller代表Action方法和Controller类级别;First和Last意味着希望被作为第一个和最后一个Filter来执行;Global代表一个全局的Filter。
通过上面的代码片断我们可以看到FilterScope的5个枚举选项均被设置了一个值,这个值决定了Filter的执行顺序,具有更小的枚举值会被优先执行。从FilterScope的定义可以得到这样的结论:对于具有相同Order属性值的多个Filter,应用在Controller上的Filter比应用在Action方法上的Filter具有更高的执行优先级,而一个全局的Filter的执行优先级又高于基于Action的Filter。
二、FilterProvider
Filter的提供机制与之前我们介绍的基于ModelBinder和ModelValidator的提供机制比较类似,均是通过相应的Provider来提供的。提供筛选器的FilterProvider实现了接口IFilterProvider,如下面的代码片断所示,该接口定义了唯一的方法GetFilters根据指定的Controller上下文和用于描述目标Action的ActionDescriptor对象获取一个Filter对象集合。
public interface IFilterProvider { IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }</div>
我们可以通过静态类型FilterProviders注册或者获取当前应用使用的FilterProvider。如下面的代码片断所示,FilterProviders具有一个类型为FilterProviderCollection的只读属性Providers,表示基于整个Web应用范围内被使用的FilterProvider列表。FilterProviderCollection是元素类型为IFilterProvider的集合,GetFilters方法用于或者该集合中所有FilterProvider对象提供的Filter对象。
public static class FilterProviders { public static FilterProviderCollection Providers { get; } } public class FilterProviderCollection : Collection<IFilterProvider> { //其他成员 public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }</div>
ASP.NET MVC提供了三种原生的FilterProvider,分别是FilterAttributeFilterProvider、ControllerInstanceFilterProvider和GlobalFilterCollection,接下来我们对它们进行单独介绍。
三、FilterAttribute与FilterAttributeFilterProvider
我们通常将筛选器定义成特性以声明的方式应用到Controller类型或者Action方法上,而抽象类型FilterAttribute是所有筛选器的基类。如下面的代码片断所示,FilterAttribute特性实现了IMvcFilter接口,该接口定义了Order和AllowMultiple两个只读属性,分别用于控制筛选器的执行顺序以及多个同类的筛选器能够同时应用到同一个目标元素(类或者方法)。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)] public abstract class FilterAttribute : Attribute, IMvcFilter { protected FilterAttribute(); public bool AllowMultiple { get; } public int Order { get; set; } } public interface IMvcFilter { bool AllowMultiple { get; } int Order { get; } }</div>
从应用在FilterAttribute上的AttributeUsageAttribute的定义可以看出该特性可以应用在类型和方法上,这意味着筛选器一般都可以应用在Controller类型和Action方法上。只读属性AllowMultiple实际上返回的是AttributeUsageAttribute的同名属性,通过上面的定义我们可以看到默认情况下该属性值为False。
用于描述Controller和Action的ControllerDescriptor和ActionDescriptor均实现了ICustomAttributeProvider接口,我们可以直接利用它们获取应用在对应的Controller类型或者Action方法上包括FilterAttribute在内的所有特性。实际上,这两个描述类型提供了单独的方法GetFilterAttributes专门用于获取FilterAttribute特性列表。如下面的代码片断所示,该方法具有一个布尔类型的参数useCache,表示是否需要对解析出来的FilterAttribute特性进行缓存以缓解频繁的反射操作对性能造成的影响。
public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable { //其他成员 public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache); } public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable { //其他成员 public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache); }</div>
针对FilterAttribute特性的Filter通过FilterAttributeFilterProvider对象来提供。FilterAttributeFilterProvider直接调用当前ControllerDescriptor和ActionDescriptor的GetFilterAttributes方法获取所有应用在Controller类型和当前Action方法的FilterAttribute特性,并借此创建相应的Filter对象。FilterAttributeFilterProvider构造函数的参数cacheAttributeInstances表示是否启用针对FilterAttribute的缓存,它将作为调用GetFilterAttributes方法的参数。在默认的情况下(通过调用默认无参的构造函数创建的FilterAttributeFilterProvider)会采用针对FilterAttribute的缓存。
public class FilterAttributeFilterProvider : IFilterProvider { public FilterAttributeFilterProvider(); public FilterAttributeFilterProvider(bool cacheAttributeInstances); protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor); protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor); public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }</div>
对于通过调用GetFilters得到的Filter,对应的FilterAttribute特性作为其Instance属性。Order属性来源于FilterAttribute的同名属性,而Scope属性则取决于FilterAttribute特性是应用在Controller类型上(Scope属性值为Controller)还是当前的Acti
您可能想查找下面的文章:
- 详解ASP.NET MVC 常用扩展点:过滤器、模型绑定
- ASP.NET MVC从视图传参到控制器的几种形式
- ASP.NET MVC 4 中的JSON数据交互的方法
- ASP.NET MVC制作404跳转实例(非302和200)
- 详解ASP.NET MVC 利用Razor引擎生成静态页
- 详解ASP.NET MVC 解析模板生成静态页(RazorEngine)
- ASP.NET MVC4 利用uploadify.js多文件上传
- ASP.NET mvc4中的过滤器的使用
- Asp.net MVC下使用Bundle合并、压缩js与css文件详解
- 详解Asp.Net MVC——控制器与动作(Controller And Action)