(转)读过滤器源码

  今天按着Artech的文章又研究了研究过滤器的源码,主要究其实现,还有一点不清晰的地方,惋惜周末得回趟家,老妈身体小样。周末只能小看一下书了。继续研究,过两天搞两个例子多试下,研究完了源码及其实现,清晰多了。要不用的总感受不太清楚。还有几篇文章也看了,如今就不贴出了,等些例子的时候会一块儿给出。html

在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?》中]编程

目录 
1、Filter 
2、FilterProvider 
3、FilterAttribute与FilterAttributeFilterProvider 
4、Controller与ControllerInstanceFilterProvider 
5、GlobalFilterCollection 
6、实例演示:验证Filter的提供机制和执行顺序浏览器

1、Filter

虽然ASP.NET MVC提供的四种类型的筛选器具备各自实现的接口,可是对于筛选器的提供体系来讲全部的筛选器都经过具备以下定义的Filter类型表示。Filter的核心是Instance属性,由于它表明真正实施筛选功能的对象,该对象实现了一个或者多个基于上述四种筛选器类型的接口。缓存

   1: public class Filter
   2: {    
   3:     public const int DefaultOrder = -1;   
   4:     public Filter(object instance, FilterScope scope, int? order);
   5:     
   6:     public object Instance { get; protected set; }
   7:     public int Order { get; protected set; }
   8:     public FilterScope Scope { get; protected set; }
   9: }
  10:  
  11: public enum FilterScope
  12: {
  13:     Action        = 30,
  14:     Controller    = 20,
  15:     First         = 0,
  16:     Global        = 10,
  17:     Last          = 100
  18: }

注:因为System.Web.Mvc.Filter和实现了IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter的类型都可以被称为“筛选器”,为了避免至于形成混淆,在没有作明确说明的状况下,咱们使用英文“Filter”和中文“筛选器”分别来表示它们。mvc

Filter的Order和Scope属性最终决定了筛选器的执行顺序。Order属性对应数值越小,执行的优先级越高,该属性的默认值为-1(对应着Filter中定义的常量DefaultOrder)。若是两个Filter具备相同的Order属性值,那么Scope属性最终决定哪一个被优先执行。Filter的Scope属性类型是一个类型为FilterScope的枚举。该枚举表示应用Filter的范围,Action和Controller表明Action方法和Controller类级别;First和Last意味着但愿被做为第一个和最后一个Filter来执行;Global表明一个全局的Filter。app

经过上面的代码片段咱们能够看到FilterScope的5个枚举选项均被设置了一个值,这个值决定了Filter的执行顺序,具备更小的枚举值会被优先执行。从FilterScope的定义能够获得这样的结论:对于具备相同Order属性值的多个Filter,应用在Controller上的Filter比应用在Action方法上的Filter具备更高的执行优先级,而一个全局的Filter的执行优先级又高于基于Action的Filteride

2、FilterProvider

Filter的提供机制与以前咱们介绍的基于ModelBinder和ModelValidator的提供机制比较相似,均是经过相应的Provider来提供的。提供筛选器的FilterProvider实现了接口IFilterProvider,以下面的代码片段所示,该接口定义了惟一的方法GetFilters根据指定的Controller上下文和用于描述目标Action的ActionDescriptor对象获取一个Filter对象集合。函数

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

咱们能够经过静态类型FilterProviders注册或者获取当前应用使用的FilterProvider。以下面的代码片段所示,FilterProviders具备一个类型为FilterProviderCollection的只读属性Providers,表示基于整个Web应用范围内被使用的FilterProvider列表。FilterProviderCollection是元素类型为IFilterProvider的集合,GetFilters方法用于或者该集合中全部FilterProvider对象提供的Filter对象。性能

   1: public static class FilterProviders
   2: {    
   3:     public static FilterProviderCollection Providers { get; }
   4: }
   5:  
   6: public class FilterProviderCollection : Collection<IFilterProvider>
   7: {   
   8:     
   9:     //其余成员
  10:     public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);   
  11: }

ASP.NET MVC提供了三种原生的FilterProvider,分别是FilterAttributeFilterProvider、ControllerInstanceFilterProvider和GlobalFilterCollection,接下来咱们对它们进行单独介绍。this

3、FilterAttribute与FilterAttributeFilterProvider

咱们一般将筛选器定义成特性以声明的方式应用到Controller类型或者Action方法上,而抽象类型FilterAttribute是全部筛选器的基类。以下面的代码片段所示,FilterAttribute特性实现了IMvcFilter接口,该接口定义了Order和AllowMultiple两个只读属性,分别用于控制筛选器的执行顺序以及多个同类的筛选器可以同时应用到同一个目标元素(类或者方法)。

   1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)]
   2: public abstract class FilterAttribute : Attribute, IMvcFilter
   3: {    
   4:     protected FilterAttribute();
   5:     
   6:     public bool AllowMultiple { get; }
   7:     public int Order { get; set; }
   8: }
   9:  
  10: public interface IMvcFilter
  11: {    
  12:     bool AllowMultiple { get; }
  13:     int Order { get; }
  14: }

从应用在FilterAttribute上的AttributeUsageAttribute的定义能够看出该特性能够应用在类型和方法上,这意味着筛选器通常均可以应用在Controller类型和Action方法上。只读属性AllowMultiple实际上返回的是AttributeUsageAttribute的同名属性,经过上面的定义咱们能够看到默认状况下该属性值为False。

用于描述Controller和Action的ControllerDescriptor和ActionDescriptor均实现了ICustomAttributeProvider接口,咱们能够直接利用它们获取应用在对应的Controller类型或者Action方法上包括FilterAttribute在内的全部特性。实际上,这两个描述类型提供了单独的方法GetFilterAttributes专门用于获取FilterAttribute特性列表。以下面的代码片段所示,该方法具备一个布尔类型的参数useCache,表示是否须要对解析出来的FilterAttribute特性进行缓存以缓解频繁的反射操做对性能形成的影响。

   1: public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
   2: {
   3:     //其余成员
   4:     public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
   5: }
   6: public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
   7: {   
   8:     //其余成员
   9:     public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);    
  10: }

针对FilterAttribute特性的Filter经过FilterAttributeFilterProvider对象来提供。FilterAttributeFilterProvider直接调用当前ControllerDescriptor和ActionDescriptor的GetFilterAttributes方法获取全部应用在Controller类型和当前Action方法的FilterAttribute特性,并借此建立相应的Filter对象。FilterAttributeFilterProvider构造函数的参数cacheAttributeInstances表示是否启用针对FilterAttribute的缓存,它将做为调用GetFilterAttributes方法的参数。在默认的状况下(经过调用默认无参的构造函数建立的FilterAttributeFilterProvider)会采用针对FilterAttribute的缓存。

   1: public class FilterAttributeFilterProvider : IFilterProvider
   2: {
   3:     public FilterAttributeFilterProvider();
   4:     public FilterAttributeFilterProvider(bool cacheAttributeInstances);
   5:     protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
   6:     protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
   7:     public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
   8: }

对于经过调用GetFilters获得的Filter,对应的FilterAttribute特性做为其Instance属性。Order属性来源于FilterAttribute的同名属性,而Scope属性则取决于FilterAttribute特性是应用在Controller类型上(Scope属性值为Controller)仍是当前的Action方法上(Scope属性值为Action)。

4、Controller与ControllerInstanceFilterProvider

提到ASP.NET MVC的筛选器,大部分的都只会想到经过FilterAttribute特性,实际上Controller自己(继承自抽象类Controller)就是一个筛选器。以下面的代码片段所示,抽象类Controller实现了IActionFilter、IAuthorizationFilter、IExceptionFilter和IResultFilter这四个对应着不一样筛选器类型的接口。

   1: public abstract class Controller : ControllerBase, 
   2:     IActionFilter, 
   3:     IAuthorizationFilter, 
   4:     IExceptionFilter, 
   5:     IResultFilter, 
   6:      ...
   7: {
   8:    //省略成员
   9: }

针对Controller对象这种独特筛选器的FilterProvider类型为具备以下定义的ControllerInstanceFilterProvider。在实现的GetFilters方法中,它会根据指定的Controller上下文获取对应的Controller对象,并以此建立一个Filter(Controller对象做为Filter对象的Instance属性值)。该Filter的Scope不是Controller,而是First,而Order的值为-2147483648(Int32.MinValue),毫无疑问这样的Filter确定第一个被执行

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

 

5、GlobalFilterCollection

经过FilterAttribute的形式定义的筛选器须要显式地标注到目标Controller类型或者Action方法上,而在有些状况下须要一种全局的Filter。所谓全局筛选器,就是不须要显式与某个Controller或者Action进行匹配,而是默认使用到全部的Action执行过程当中。用于提供这种全局Filter的FilterProvider对应的类型为具备以下定义的GlobalFilterCollection

   1: public sealed class GlobalFilterCollection : IEnumerable<Filter>, IEnumerable, IFilterProvider
   2: {
   3:     public GlobalFilterCollection();
   4:     public void Add(object filter);
   5:     public void Add(object filter, int order);
   6:     private void AddInternal(object filter, int? order);
   7:     public void Clear();
   8:     public bool Contains(object filter);
   9:     public IEnumerator<Filter> GetEnumerator();
  10:     public void Remove(object filter);
  11:     IEnumerator IEnumerable.GetEnumerator();
  12:     IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  13:  
  14:     public int Count { get; }
  15: }

经过命名以及上面给出的定义能够看出GlobalFilterCollection就是一个Filter的列表而已,实现的GetFilters方法返回的就是它本身。经过GlobalFilterCollection提供的方法咱们能够实现对全局Filter的添加、删除和清除操做。用于添加Filter的Add方法的参数filter不是一个Filter对象,而是一个具体筛选器(实现了相应的筛选器接口),添加的Filter对象根据该筛选器对象建立,其Scope属性被设置成Global。咱们经过在Add方法指定添加Filter对象的Order属性,若是没有显示指定Order而且指定的筛选器是一个FilterAttribute特性,那么该特性的Order将会做为Filter对象的Order;不然使用-1做为Order属性值。

针对整个Web应用的全局Filter(或者说全局FilterProvider)的注册和获取能够经过静态类型GlobalFilters来实现。以下面的代码片段所示,GlobalFilters具备一个静态只读属性Filters返回一个GlobalFilterCollection对象。

   1: public static class GlobalFilters
   2: {    
   3:     public static GlobalFilterCollection Filters { get; }
   4: }

到目前为止,咱们已经介绍了ASP.NET MVC默认提供的三种FilterProvider,以及各自采用得Filter提供机制。当用于注册FilterProvider的静态类型在加载的时候,会默认建立这三种类型的对象并将其做为表示全局FilterProvider集合的Providers属性值,具体的逻辑体如今以下的代码片段中。也就是说,在默认的状况下ASP.NET MVC会采用这三种FilterProvider来提供全部的Filter对象。

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

 

6、实例演示:验证Filter的提供机制和执行顺序

为了让读者对上面介绍的Filter提供机制具备一个更加深入的映像,咱们来作一个简单的实例演示。在一个经过Visual Studio的ASP.NET MVC项目模板建立的空Web项目中,咱们定义了以下一个几个FilterAttribute。FilterBaseAttribute是一个实现了IActionFilter接口的抽象类型,三个具体的FilterAttribute(FooAttribute、BarAttribute和BazAttribute)是它的继承者。

   1: public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter
   2: {
   3:     public void OnActionExecuted(ActionExecutedContext filterContext)
   4:     {}
   5:  
   6:     public void OnActionExecuting(ActionExecutingContext filterContext)
   7:     {}
   8: }
   9:  
  10: public class FooAttribute : FilterBaseAttribute
  11: {}
  12: public class BarAttribute : FilterBaseAttribute
  13: {}
  14: public class BazAttribute : FilterBaseAttribute
  15: {} 

咱们首先在Global.asax中经过以下的方式将BazAttribute注册为一个全局筛选器。须要注意的是定义在默认建立的Global.asax中的Application_Start方法会调用RegisterGlobalFilters方法注册一个类型为HandleErrorAttribute的ExceptionFilter,咱们须要将这行代码注释。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     //其余成员
   4:     protected void Application_Start()
   5:     {        
   6:         //其余操做
   7:         //RegisterGlobalFilters(GlobalFilters.Filters);        
   8:         GlobalFilters.Filters.Add(new BazAttribute());
   9:     }
  10: }

最后咱们建立以下一个默认的HomeController,一个空的Action方法Data上应用了咱们定义的BarAttribute特性,而HomeController类上则应用了FooAttribute特性。在默认的Action方法Index中,咱们经过FilterProviders的静态属性Providers表示的全局FilterProvider列表获得针对于Action方法Data的全部Filter对象,并将它们的基本信息(类型、Order和Scope属性)呈现出来。

   1: [Foo]
   2: public class HomeController : Controller
   3: {
   4:     public void Index()
   5:     {
   6:         ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));
   7:         ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Data");
   8:         foreach (var filter in FilterProviders.Providers.GetFilters(ControllerContext, actionDescriptor))
   9:         { 
  10:             Response.Write(string.Format("{0}<br/>",filter.Instance));
  11:             Response.Write(string.Format("&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/>", "Order",filter.Order));
  12:             Response.Write(string.Format("&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/><br/>", "Scope",filter.Scope));
  13:         }
  14:     }
  15:     [Bar]
  16:     public void Data()
  17:     { }
  18: }

运行咱们的程序以后会在浏览器中呈现如图7-5所示的结果。咱们能够清楚地看到,不只仅应用在自身Action方法的FilterAttribute会应用到目标Action上,应用在Controller类的FilterAttribute、全局注册的Filter以及Controller对象自己体现的Filter都回最终应用在全部的Action上面。

image

上图将应用于Action方法Data的4个Filter的Order和Scope属性显示出来。咱们在前面提到这两个属性决定了同类筛选器执行的顺序,咱们如今利用这个程序要证明这一点。为此咱们须要对FilterBaseAttribute做以下的修改,在OnActionExecuting中咱们将当前执行的FilterAttribute的类型的方法名呈现出来。

   1: public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter
   2: {
   3:     public void OnActionExecuted(ActionExecutedContext filterContext)
   4:     {}
   5:     public void OnActionExecuting(ActionExecutingContext filterContext)
   6:     {
   7:         filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuting()<br/>", this.GetType()));
   8:     }
   9: }

而后咱们按照相同的方式重写了HomeController的OnActionExecuting方法,将HomeController自身的类型的当前方法名称呈现出来。

   1: [Foo]
   2: public class HomeController : Controller
   3: {
   4:     //其余成员
   5:     protected override void OnActionExecuting(ActionExecutingContext filterContext)
   6:     {
   7:         Response.Write("HomeController.OnActionExecuting()<br/>");
   8:     }
   9:  
  10:     [Bar]
  11:     public void Data()
  12:     { }
  13: }

咱们再次运行咱们的程序,并在浏览器上指定正确的地址访问定义在HomeController的Action方法Data,会在浏览器中呈现以下图所示的结果。输出的结果体现了应用到Action方法Data上的四个ActionFilter执行的顺序,而这是和Filter对应的Order和Scope属性值是一致的。

image

关于Filter的提供还另外一个值得深究的问题:咱们在定义FilterAttribute的时候能够将应用在该类型上的AttributeUsageAttribute的AllowMultiple属性设置为False使它只能在同一个目标元素上应用一次。可是,咱们依然能够在Action方法和所在的Controller类型上应用它们,甚至能够将它们注册为全局Filter,那么这些FilterAttribute都将有效吗

咱们如今就来经过实例来验证这一点。如今咱们删除全部的FilterAttribute,定义以下一个类型为FooAttribute的ActionFilter,咱们将应用在它上面的AttributeUsageAttribute特性的AllowMultiple属性设置为False。

   1: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
   2: public class FooAttribute : FilterAttribute, IActionFilter
   3: {
   4:     public void OnActionExecuted(ActionExecutedContext filterContext)
   5:     { }
   6:     public void OnActionExecuting(ActionExecutingContext filterContext)
   7:     { }
   8: }

如今咱们将该FooAttribute特性同时应用在HomeController类型和Action方法Data上,而后在Global.asax中注册一个针对FooAttribute特性的全局Filter。

   1: [Foo]
   2: public class HomeController : Controller
   3: {
   4:     //其余成员
   5:     [Foo]
   6:     public void Data()
   7:     { }
   8:  
   9: }
  10:  
  11: public class MvcApplication : System.Web.HttpApplication
  12: {
  13:     //其余成员
  14:     protected void Application_Start()
  15:     {
  16:         //其余操做
  17:         //RegisterGlobalFilters(GlobalFilters.Filters);
  18:         GlobalFilters.Filters.Add(new FooAttribute());
  19:     }
  20: }

如今咱们直接运行咱们的程序,开启的浏览器中会呈现出如图7-7所示的结果。能够清楚地看到虽然咱们 在三个地方注册了FooAttribute,可是因为该特性的AllowMultiple属性为False,因此只有其中一个FooAttribute最终是有效的。

image

对于AllowMultiple属性为False的FilterAttribute来讲,若是咱们以不一样的Scope注册了多个,最终有效的是哪一个呢?从上图能够看出,应用在Action方法(Scope为Action)上的FooAttribute是有效的。其实具体的逻辑是这样的:全部被建立的Filter按照Order+Scope进行排序(即Filter执行的顺序),取排在最后一个。对于咱们的例子来讲,提供的三个Filter具备相同的Order属性值(-1),全部最终会按照Scope(Scope、Controller和Action)进行排序,排在最后一个的天然是Scope为Action的Filter。

 

原文(http://www.cnblogs.com/artech/archive/2012/07/02/filter.html

相关文章
相关标签/搜索