(轉)讀過濾器源碼

  今天按着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

相關文章
相關標籤/搜索