溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ASP.NET MVC的篩選器詳細(xì)介紹

發(fā)布時(shí)間:2021-09-13 14:44:39 來源:億速云 閱讀:146 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要講解了“ASP.NET MVC的篩選器詳細(xì)介紹”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“ASP.NET MVC的篩選器詳細(xì)介紹”吧!

目錄

一、Filter

二、FilterProvider

三、FilterAttribute與FilterAttributeFilterProvider

四、Controller與ControllerInstanceFilterProvider

五、GlobalFilterCollection

六、實(shí)例演示:驗(yàn)證Filter的提供機(jī)制和執(zhí)行順序

一、Filter

雖然ASP.NET MVC提供的四種類型的篩選器具有各自實(shí)現(xiàn)的接口,但是對于篩選器的提供體系來說所有的篩選器都通過具有如下定義的Filter類型表示。Filter的核心是Instance屬性,因?yàn)樗碚嬲龑?shí)施篩選功能的對象,該對象實(shí)現(xiàn)了一個(gè)或者多個(gè)基于上述四種篩選器類型的接口。

  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和實(shí)現(xiàn)了IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter的類型均可以被稱為“篩選器”,為了不至于造成混淆,在沒有做明確說明的情況下,我們使用英文“Filter”和中文“篩選器”分別來表示它們。

Filter的Order和Scope屬性最終決定了篩選器的執(zhí)行順序。Order屬性對應(yīng)數(shù)值越小,執(zhí)行的優(yōu)先級越高,該屬性的默認(rèn)值為-1(對應(yīng)著Filter中定義的常量DefaultOrder)。如果兩個(gè)Filter具有相同的Order屬性值,那么Scope屬性最終決定哪個(gè)被優(yōu)先執(zhí)行。Filter的Scope屬性類型是一個(gè)類型為FilterScope的枚舉。該枚舉表示應(yīng)用Filter的范圍,Action和Controller代表Action方法和Controller類級別;First和Last意味著希望被作為第一個(gè)和最后一個(gè)Filter來執(zhí)行;Global代表一個(gè)全局的Filter。

通過上面的代碼片斷我們可以看到FilterScope的5個(gè)枚舉選項(xiàng)均被設(shè)置了一個(gè)值,這個(gè)值決定了Filter的執(zhí)行順序,具有更小的枚舉值會被優(yōu)先執(zhí)行。從FilterScope的定義可以得到這樣的結(jié)論:對于具有相同Order屬性值的多個(gè)Filter,應(yīng)用在Controller上的Filter比應(yīng)用在Action方法上的Filter具有更高的執(zhí)行優(yōu)先級,而一個(gè)全局的Filter的執(zhí)行優(yōu)先級又高于基于Action的Filter。

二、FilterProvider

Filter的提供機(jī)制與之前我們介紹的基于ModelBinder和ModelValidator的提供機(jī)制比較類似,均是通過相應(yīng)的Provider來提供的。提供篩選器的FilterProvider實(shí)現(xiàn)了接口IFilterProvider,如下面的代碼片斷所示,該接口定義了唯一的方法GetFilters根據(jù)指定的Controller上下文和用于描述目標(biāo)Action的ActionDescriptor對象獲取一個(gè)Filter對象集合。

  public interface IFilterProvider
  {  
    IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  }

我們可以通過靜態(tài)類型FilterProviders注冊或者獲取當(dāng)前應(yīng)用使用的FilterProvider。如下面的代碼片斷所示,F(xiàn)ilterProviders具有一個(gè)類型為FilterProviderCollection的只讀屬性Providers,表示基于整個(gè)Web應(yīng)用范圍內(nèi)被使用的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,接下來我們對它們進(jìn)行單獨(dú)介紹。

三、FilterAttribute與FilterAttributeFilterProvider

我們通常將篩選器定義成特性以聲明的方式應(yīng)用到Controller類型或者Action方法上,而抽象類型FilterAttribute是所有篩選器的基類。如下面的代碼片斷所示,F(xiàn)ilterAttribute特性實(shí)現(xiàn)了IMvcFilter接口,該接口定義了Order和AllowMultiple兩個(gè)只讀屬性,分別用于控制篩選器的執(zhí)行順序以及多個(gè)同類的篩選器能夠同時(shí)應(yīng)用到同一個(gè)目標(biāo)元素(類或者方法)。

  [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; }
  }

從應(yīng)用在FilterAttribute上的AttributeUsageAttribute的定義可以看出該特性可以應(yīng)用在類型和方法上,這意味著篩選器一般都可以應(yīng)用在Controller類型和Action方法上。只讀屬性AllowMultiple實(shí)際上返回的是AttributeUsageAttribute的同名屬性,通過上面的定義我們可以看到默認(rèn)情況下該屬性值為False。

用于描述Controller和Action的ControllerDescriptor和ActionDescriptor均實(shí)現(xiàn)了ICustomAttributeProvider接口,我們可以直接利用它們獲取應(yīng)用在對應(yīng)的Controller類型或者Action方法上包括FilterAttribute在內(nèi)的所有特性。實(shí)際上,這兩個(gè)描述類型提供了單獨(dú)的方法GetFilterAttributes專門用于獲取FilterAttribute特性列表。如下面的代碼片斷所示,該方法具有一個(gè)布爾類型的參數(shù)useCache,表示是否需要對解析出來的FilterAttribute特性進(jìn)行緩存以緩解頻繁的反射操作對性能造成的影響。

  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直接調(diào)用當(dāng)前ControllerDescriptor和ActionDescriptor的GetFilterAttributes方法獲取所有應(yīng)用在Controller類型和當(dāng)前Action方法的FilterAttribute特性,并借此創(chuàng)建相應(yīng)的Filter對象。FilterAttributeFilterProvider構(gòu)造函數(shù)的參數(shù)cacheAttributeInstances表示是否啟用針對FilterAttribute的緩存,它將作為調(diào)用GetFilterAttributes方法的參數(shù)。在默認(rèn)的情況下(通過調(diào)用默認(rèn)無參的構(gòu)造函數(shù)創(chuàng)建的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);
  }

對于通過調(diào)用GetFilters得到的Filter,對應(yīng)的FilterAttribute特性作為其Instance屬性。Order屬性來源于FilterAttribute的同名屬性,而Scope屬性則取決于FilterAttribute特性是應(yīng)用在Controller類型上(Scope屬性值為Controller)還是當(dāng)前的Action方法上(Scope屬性值為Action)。

四、Controller與ControllerInstanceFilterProvider

提到ASP.NET MVC的篩選器,大部分的都只會想到通過FilterAttribute特性,實(shí)際上Controller本身(繼承自抽象類Controller)就是一個(gè)篩選器。如下面的代碼片斷所示,抽象類Controller實(shí)現(xiàn)了IActionFilter、IAuthorizationFilter、IExceptionFilter和IResultFilter這四個(gè)對應(yīng)著不同篩選器類型的接口。

  public abstract class Controller : ControllerBase, 
    IActionFilter, 
    IAuthorizationFilter, 
    IExceptionFilter, 
    IResultFilter, 
     ...
  {
    //省略成員
  }

針對Controller對象這種獨(dú)特篩選器的FilterProvider類型為具有如下定義的ControllerInstanceFilterProvider。在實(shí)現(xiàn)的GetFilters方法中,它會根據(jù)指定的Controller上下文獲取對應(yīng)的Controller對象,并以此創(chuàng)建一個(gè)Filter(Controller對象作為Filter對象的Instance屬性值)。該Filter的Scope不是Controller,而是First,而Order的值為-2147483648(Int32.MinValue),毫無疑問這樣的Filter肯定第一個(gè)被執(zhí)行。

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

五、GlobalFilterCollection

通過FilterAttribute的形式定義的篩選器需要顯式地標(biāo)注到目標(biāo)Controller類型或者Action方法上,而在有些情況下需要一種全局的Filter。所謂全局篩選器,就是不需要顯式與某個(gè)Controller或者Action進(jìn)行匹配,而是默認(rèn)使用到所有的Action執(zhí)行過程中。用于提供這種全局Filter的FilterProvider對應(yīng)的類型為具有如下定義的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就是一個(gè)Filter的列表而已,實(shí)現(xiàn)的GetFilters方法返回的就是它自己。通過GlobalFilterCollection提供的方法我們可以實(shí)現(xiàn)對全局Filter的添加、刪除和清除操作。用于添加Filter的Add方法的參數(shù)filter不是一個(gè)Filter對象,而是一個(gè)具體篩選器(實(shí)現(xiàn)了相應(yīng)的篩選器接口),添加的Filter對象根據(jù)該篩選器對象創(chuàng)建,其Scope屬性被設(shè)置成Global。我們通過在Add方法指定添加Filter對象的Order屬性,如果沒有顯示指定Order并且指定的篩選器是一個(gè)FilterAttribute特性,那么該特性的Order將會作為Filter對象的Order;否則使用-1作為Order屬性值。

針對整個(gè)Web應(yīng)用的全局Filter(或者說全局FilterProvider)的注冊和獲取可以通過靜態(tài)類型GlobalFilters來實(shí)現(xiàn)。如下面的代碼片斷所示,GlobalFilters具有一個(gè)靜態(tài)只讀屬性Filters返回一個(gè)GlobalFilterCollection對象。

  public static class GlobalFilters
  {  
    public static GlobalFilterCollection Filters { get; }
  }

到目前為止,我們已經(jīng)介紹了ASP.NET MVC默認(rèn)提供的三種FilterProvider,以及各自采用得Filter提供機(jī)制。當(dāng)用于注冊FilterProvider的靜態(tài)類型在加載的時(shí)候,會默認(rèn)創(chuàng)建這三種類型的對象并將其作為表示全局FilterProvider集合的Providers屬性值,具體的邏輯體現(xiàn)在如下的代碼片斷中。也就是說,在默認(rèn)的情況下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;}
  }

六、實(shí)例演示:驗(yàn)證Filter的提供機(jī)制和執(zhí)行順序

為了讓讀者對上面介紹的Filter提供機(jī)制具有一個(gè)更加深刻的映像,我們來做一個(gè)簡單的實(shí)例演示。在一個(gè)通過Visual Studio的ASP.NET MVC項(xiàng)目模板創(chuàng)建的空Web項(xiàng)目中,我們定義了如下一個(gè)幾個(gè)FilterAttribute。FilterBaseAttribute是一個(gè)實(shí)現(xiàn)了IActionFilter接口的抽象類型,三個(gè)具體的FilterAttribute(FooAttribute、BarAttribute和BazAttribute)是它的繼承者。

  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注冊為一個(gè)全局篩選器。需要注意的是定義在默認(rèn)創(chuàng)建的Global.asax中的Application_Start方法會調(diào)用RegisterGlobalFilters方法注冊一個(gè)類型為HandleErrorAttribute的ExceptionFilter,我們需要將這行代碼注釋。

  public class MvcApplication : System.Web.HttpApplication
  {
    //其他成員
    protected void Application_Start()
    {    
      //其他操作
      //RegisterGlobalFilters(GlobalFilters.Filters);    
      GlobalFilters.Filters.Add(new BazAttribute());
    }
  }

最后我們創(chuàng)建如下一個(gè)默認(rèn)的HomeController,一個(gè)空的Action方法Data上應(yīng)用了我們定義的BarAttribute特性,而HomeController類上則應(yīng)用了FooAttribute特性。在默認(rèn)的Action方法Index中,我們通過FilterProviders的靜態(tài)屬性Providers表示的全局FilterProvider列表得到針對于Action方法Data的所有Filter對象,并將它們的基本信息(類型、Order和Scope屬性)呈現(xiàn)出來。

  [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("&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/>", "Order",filter.Order));
        Response.Write(string.Format("&nbsp;&nbsp;&nbsp;&nbsp;{0}: {1}<br/><br/>", "Scope",filter.Scope));
      }
    }
    [Bar]
    public void Data()
    { }
  }

運(yùn)行我們的程序之后會在瀏覽器中呈現(xiàn)如圖7-5所示的結(jié)果。我們可以清楚地看到,不僅僅應(yīng)用在自身Action方法的FilterAttribute會應(yīng)用到目標(biāo)Action上,應(yīng)用在Controller類的FilterAttribute、全局注冊的Filter以及Controller對象本身體現(xiàn)的Filter都回最終應(yīng)用在所有的Action上面。

ASP.NET MVC的篩選器詳細(xì)介紹

上圖將應(yīng)用于Action方法Data的4個(gè)Filter的Order和Scope屬性顯示出來。我們在前面提到這兩個(gè)屬性決定了同類篩選器執(zhí)行的順序,我們現(xiàn)在利用這個(gè)程序要證實(shí)這一點(diǎn)。為此我們需要對FilterBaseAttribute作如下的修改,在OnActionExecuting中我們將當(dāng)前執(zhí)行的FilterAttribute的類型的方法名呈現(xiàn)出來。

  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自身的類型的當(dāng)前方法名稱呈現(xiàn)出來。

  [Foo]
  public class HomeController : Controller
  {
    //其他成員
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      Response.Write("HomeController.OnActionExecuting()<br/>");
    } 
    [Bar]
    public void Data()
    { }
  }

我們再次運(yùn)行我們的程序,并在瀏覽器上指定正確的地址訪問定義在HomeController的Action方法Data,會在瀏覽器中呈現(xiàn)如下圖所示的結(jié)果。輸出的結(jié)果體現(xiàn)了應(yīng)用到Action方法Data上的四個(gè)ActionFilter執(zhí)行的順序,而這是和Filter對應(yīng)的Order和Scope屬性值是一致的。

ASP.NET MVC的篩選器詳細(xì)介紹

關(guān)于Filter的提供還另一個(gè)值得深究的問題:我們在定義FilterAttribute的時(shí)候可以將應(yīng)用在該類型上的AttributeUsageAttribute的AllowMultiple屬性設(shè)置為False使它只能在同一個(gè)目標(biāo)元素上應(yīng)用一次。但是,我們依然可以在Action方法和所在的Controller類型上應(yīng)用它們,甚至可以將它們注冊為全局Filter,那么這些FilterAttribute都將有效嗎?

我們現(xiàn)在就來通過實(shí)例來驗(yàn)證這一點(diǎn)?,F(xiàn)在我們刪除所有的FilterAttribute,定義如下一個(gè)類型為FooAttribute的ActionFilter,我們將應(yīng)用在它上面的AttributeUsageAttribute特性的AllowMultiple屬性設(shè)置為False。

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
  public class FooAttribute : FilterAttribute, IActionFilter
  {
    public void OnActionExecuted(ActionExecutedContext filterContext)
    { }
    public void OnActionExecuting(ActionExecutingContext filterContext)
    { }
  }

現(xiàn)在我們將該FooAttribute特性同時(shí)應(yīng)用在HomeController類型和Action方法Data上,然后在Global.asax中注冊一個(gè)針對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());
    }
  }

現(xiàn)在我們直接運(yùn)行我們的程序,開啟的瀏覽器中會呈現(xiàn)出如圖7-7所示的結(jié)果??梢郧宄乜吹诫m然我們 在三個(gè)地方注冊了FooAttribute,但是由于該特性的AllowMultiple屬性為False,所以只有其中一個(gè)FooAttribute最終是有效的。

ASP.NET MVC的篩選器詳細(xì)介紹

對于AllowMultiple屬性為False的FilterAttribute來說,如果我們以不同的Scope注冊了多個(gè),最終有效的是哪個(gè)呢?從上圖可以看出,應(yīng)用在Action方法(Scope為Action)上的FooAttribute是有效的。其實(shí)具體的邏輯是這樣的:所有被創(chuàng)建的Filter按照Order+Scope進(jìn)行排序(即Filter執(zhí)行的順序),取排在最后一個(gè)。對于我們的例子來說,提供的三個(gè)Filter具有相同的Order屬性值(-1),所有最終會按照Scope(Scope、Controller和Action)進(jìn)行排序,排在最后一個(gè)的自然是Scope為Action的Filter。

感謝各位的閱讀,以上就是“ASP.NET MVC的篩選器詳細(xì)介紹”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對ASP.NET MVC的篩選器詳細(xì)介紹這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI