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}注:由于System.Web.Mvc.Filter和实现了IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter的类型均可以被称为“筛选器”,为了不至于造成混淆,在没有做明确说明的情况下,我们使用英文“Filter”和中文“筛选器”分别来表示它们。
public interface IFilterProvider{IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);}我们可以通过静态类型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);}ASP.NET MVC提供了三种原生的FilterProvider,分别是FilterAttributeFilterProvider、ControllerInstanceFilterProvider和GlobalFilterCollection,接下来我们对它们进行单独介绍。
[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。
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)。
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
public sealed class GlobalFilterCollection : IEnumerable<Filter>, IEnumerable, IFilterProvider{public GlobalFilterCollection();public void Add(object filter);public void Add(object filter, int order);private void AddInternal(object filter, int? order);public void Clear();public bool Contains(object filter);public IEnumerator<Filter> GetEnumerator();public void Remove(object filter);IEnumerator IEnumerable.GetEnumerator();IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);public int Count { get; }}通过命名以及上面给出的定义可以看出GlobalFilterCollection就是一个Filter的列表而已,实现的GetFilters方法返回的就是它自己。通过GlobalFilterCollection提供的方法我们可以实现对全局Filter的添加、删除和清除操作。用于添加Filter的Add方法的参数filter不是一个Filter对象,而是一个具体筛选器(实现了相应的筛选器接口),添加的Filter对象根据该筛选器对象创建,其Scope属性被设置成Global。我们通过在Add方法指定添加Filter对象的Order属性,如果没有显示指定Order并且指定的筛选器是一个FilterAttribute特性,那么该特性的Order将会作为Filter对象的Order;否则使用-1作为Order属性值。
public static class GlobalFilters{public static GlobalFilterCollection Filters { get; }}到目前为止,我们已经介绍了ASP.NET MVC默认提供的三种FilterProvider,以及各自采用得Filter提供机制。当用于注册FilterProvider的静态类型在加载的时候,会默认创建这三种类型的对象并将其作为表示全局FilterProvider集合的Providers属性值,具体的逻辑体现在如下的代码片断中。也就是说,在默认的情况下ASP.NET MVC会采用这三种FilterProvider来提供所有的Filter对象。
public static class FilterProviders{ static FilterProviders(){Providers = new FilterProviderCollection();Providers.Add(GlobalFilters.Filters);Providers.Add(new FilterAttributeFilterProvider());Providers.Add(new ControllerInstanceFilterProvider());} public static FilterProviderCollection Providers{get;private set;}}六、实例演示:验证Filter的提供机制和执行顺序
public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter{public void OnActionExecuted(ActionExecutedContext filterContext){} public void OnActionExecuting(ActionExecutingContext filterContext){}} public class FooAttribute : FilterBaseAttribute{}public class BarAttribute : FilterBaseAttribute{}public class BazAttribute : FilterBaseAttribute{}我们首先在Global.asax中通过如下的方式将BazAttribute注册为一个全局筛选器。需要注意的是定义在默认创建的Global.asax中的Application_Start方法会调用RegisterGlobalFilters方法注册一个类型为HandleErrorAttribute的ExceptionFilter,我们需要将这行代码注释。
public class MvcApplication : System.Web.HttpApplication{//其他成员protected void Application_Start(){//其他操作//RegisterGlobalFilters(GlobalFilters.Filters);GlobalFilters.Filters.Add(new BazAttribute());}}最后我们创建如下一个默认的HomeController,一个空的Action方法Data上应用了我们定义的BarAttribute特性,而HomeController类上则应用了FooAttribute特性。在默认的Action方法Index中,我们通过FilterProviders的静态属性Providers表示的全局FilterProvider列表得到针对于Action方法Data的所有Filter对象,并将它们的基本信息(类型、Order和Scope属性)呈现出来。
[Foo]public class HomeController : Controller{public void Index(){ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Data");foreach (var filter in FilterProviders.Providers.GetFilters(ControllerContext, actionDescriptor)){ Response.Write(string.Format("{0}<br/>",filter.Instance));Response.Write(string.Format(" {0}: {1}<br/>", "Order",filter.Order));Response.Write(string.Format(" {0}: {1}<br/><br/>", "Scope",filter.Scope));}}[Bar]public void Data(){ }}运行我们的程序之后会在浏览器中呈现如图7-5所示的结果。我们可以清楚地看到,不仅仅应用在自身Action方法的FilterAttribute会应用到目标Action上,应用在Controller类的FilterAttribute、全局注册的Filter以及Controller对象本身体现的Filter都回最终应用在所有的Action上面。
上图将应用于Action方法Data的4个Filter的Order和Scope属性显示出来。我们在前面提到这两个属性决定了同类筛选器执行的顺序,我们现在利用这个程序要证实这一点。为此我们需要对FilterBaseAttribute作如下的修改,在OnActionExecuting中我们将当前执行的FilterAttribute的类型的方法名呈现出来。
public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter{public void OnActionExecuted(ActionExecutedContext filterContext){}public void OnActionExecuting(ActionExecutingContext filterContext){filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuting()<br/>", this.GetType()));}}然后我们按照相同的方式重写了HomeController的OnActionExecuting方法,将HomeController自身的类型的当前方法名称呈现出来。
[Foo]public class HomeController : Controller{//其他成员protected override void OnActionExecuting(ActionExecutingContext filterContext){Response.Write("HomeController.OnActionExecuting()<br/>");} [Bar]public void Data(){ }}我们再次运行我们的程序,并在浏览器上指定正确的地址访问定义在HomeController的Action方法Data,会在浏览器中呈现如下图所示的结果。输出的结果体现了应用到Action方法Data上的四个ActionFilter执行的顺序,而这是和Filter对应的Order和Scope属性值是一致的。
关于Filter的提供还另一个值得深究的问题:我们在定义FilterAttribute的时候可以将应用在该类型上的AttributeUsageAttribute的AllowMultiple属性设置为False使它只能在同一个目标元素上应用一次。但是,我们依然可以在Action方法和所在的Controller类型上应用它们,甚至可以将它们注册为全局Filter,那么这些FilterAttribute都将有效吗?
我们现在就来通过实例来验证这一点。现在我们删除所有的FilterAttribute,定义如下一个类型为FooAttribute的ActionFilter,我们将应用在它上面的AttributeUsageAttribute特性的AllowMultiple属性设置为False。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]public class FooAttribute : FilterAttribute, IActionFilter{public void OnActionExecuted(ActionExecutedContext filterContext){ }public void OnActionExecuting(ActionExecutingContext filterContext){ }}现在我们将该FooAttribute特性同时应用在HomeController类型和Action方法Data上,然后在Global.asax中注册一个针对FooAttribute特性的全局Filter。
[Foo]public class HomeController : Controller{//其他成员[Foo]public void Data(){ }} public class MvcApplication : System.Web.HttpApplication{//其他成员protected void Application_Start(){//其他操作//RegisterGlobalFilters(GlobalFilters.Filters);GlobalFilters.Filters.Add(new FooAttribute());}}现在我们直接运行我们的程序,开启的浏览器中会呈现出如图7-7所示的结果。可以清楚地看到虽然我们 在三个地方注册了FooAttribute,但是由于该特性的AllowMultiple属性为False,所以只有其中一个FooAttribute最终是有效的。
对于AllowMultiple属性为False的FilterAttribute来说,如果我们以不同的Scope注册了多个,最终有效的是哪个呢?从上图可以看出,应用在Action方法(Scope为Action)上的FooAttribute是有效的。其实具体的逻辑是这样的:所有被创建的Filter按照Order+Scope进行排序(即Filter执行的顺序),取排在最后一个。对于我们的例子来说,提供的三个Filter具有相同的Order属性值(-1),所有最终会按照Scope(Scope、Controller和Action)进行排序,排在最后一个的自然是Scope为Action的Filter。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持脚本之家!