[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; } }
从应用在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); }
针对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); }
对于通过调用GetFilters得到的Filter,对应的FilterAttribute特性作为其Instance属性。Order属性来源于FilterAttribute的同名属性,而Scope属性则取决于FilterAttribute特性是应用在Controller类型上(Scope属性值为Controller)还是当前的Action方法上(Scope属性值为Action)。
四、Controller与ControllerInstanceFilterProvider
提到ASP.NET MVC的筛选器,大部分的都只会想到通过FilterAttribute特性,实际上Controller本身(继承自抽象类Controller)就是一个筛选器。如下面的代码片断所示,抽象类Controller实现了IActionFilter、IAuthorizationFilter、IExceptionFilter和IResultFilter这四个对应着不同筛选器类型的接口。
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IExceptionFilter, IResultFilter, ... { //省略成员 }
针对Controller对象这种独特筛选器的FilterProvider类型为具有如下定义的ControllerInstanceFilterProvider。在实现的GetFilters方法中,它会根据指定的Controller上下文获取对应的Controller对象,并以此创建一个Filter(Controller对象作为Filter对象的Instance属性值)。该Filter的Scope不是Controller,而是First,而Order的值为-2147483648(Int32.MinValue),毫无疑问这样的Filter肯定第一个被执行。
public class ControllerInstanceFilterProvider : IFilterProvider { public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }
五、GlobalFilterCollection
通过FilterAttribute的形式定义的筛选器需要显式地标注到目标Controller类型或者Action方法上,而在有些情况下需要一种全局的Filter。所谓全局筛选器,就是不需要显式与某个Controller或者Action进行匹配,而是默认使用到所有的Action执行过程中。用于提供这种全局Filter的FilterProvider对应的类型为具有如下定义的GlobalFilterCollection。