ASP.NET Core MVC 之过滤器(Filter)
ASP.NET MVC 中的過(guò)濾器允許在執(zhí)行管道中的特定階段之前或之后運(yùn)行代碼。可以對(duì)全局,也可以對(duì)每個(gè)控制器或每個(gè)操作配置過(guò)濾器。
1.過(guò)濾器如何工作
不同的過(guò)濾器類(lèi)型在管道中的不同階段執(zhí)行,因此具有各自的與其場(chǎng)景。根據(jù)需要執(zhí)行的任務(wù)以及需要執(zhí)行的請(qǐng)求管道中的位置,選擇要?jiǎng)?chuàng)建的過(guò)濾器類(lèi)型。過(guò)濾器在 MVC 操作調(diào)用管道中運(yùn)行,有時(shí)也稱(chēng)為過(guò)濾管道,在 MVC 中選擇要執(zhí)行的操作后,執(zhí)行操作上的過(guò)濾器,如圖:
不同的過(guò)濾器在管道內(nèi)的不同位置執(zhí)行。像授權(quán)過(guò)濾器這樣的過(guò)濾器只在管道中靠前的位置執(zhí)行。其他過(guò)濾器,如操作(Action)過(guò)濾器,可以在管道執(zhí)行的其他部分之前和之后執(zhí)行,如圖:
1.選擇過(guò)濾器
授權(quán)過(guò)濾器用于確定當(dāng)前請(qǐng)求用戶(hù)是否被授權(quán)。
資源過(guò)濾器是在授權(quán)之后第一個(gè)處理請(qǐng)求的過(guò)濾器,也是最后一個(gè)在請(qǐng)求離開(kāi)過(guò)濾管道時(shí)接觸請(qǐng)求的過(guò)濾器。在性能方面,對(duì)實(shí)現(xiàn)緩存或者對(duì)過(guò)濾管道進(jìn)行短路 特別有用。
操作過(guò)濾器包裝對(duì)單個(gè)操作方法的調(diào)用,并且可以處理傳遞到操作的參數(shù)以及從操作返回的操作結(jié)果。
異常過(guò)濾器用于對(duì) MVC 應(yīng)用程序中未處理的異常應(yīng)用全局策略。
結(jié)果過(guò)濾器包裝單個(gè)操作結(jié)果的執(zhí)行,并且盡在操作執(zhí)行成功時(shí)運(yùn)行。它們必須是圍繞視圖執(zhí)行或格式化程序執(zhí)行的邏輯的理想選擇。
2.實(shí)現(xiàn)過(guò)濾器
所有過(guò)濾器均可通過(guò)不同的接口定義支持同步和異步的實(shí)現(xiàn)。根據(jù)需要執(zhí)行的任務(wù)類(lèi)型,選擇同步或異步實(shí)現(xiàn)。從框架的角度看,它們是可以互換的。
同步過(guò)濾器定義了 OnStageExecuting 和?OnStageExecuted 方法(也有例外)。OnStageExecuting 方法在事件管道階段之前通過(guò)階段名稱(chēng)來(lái)調(diào)用,而?OnStageExecuted 方法將在階段名稱(chēng)命名的管道階段之后調(diào)用。
public class SampleActionFilter:IActionFilter{public void OnActionExecuting(ActionExecutingContext context){//操作執(zhí)行前做的事情 }public void OnActionExecuted(ActionExecutedContext context){//操作執(zhí)行后做的事情 }}異步過(guò)濾器定義了一個(gè)單一的 OnActionExecutionAsync? 方法,可以在具體管道階段的前后運(yùn)行。?OnActionExecutionAsync 方法提供了一個(gè)?ActionExecutionDelegate 委托,調(diào)用時(shí)該委托會(huì)執(zhí)行具體管道階段的工作,然后等待完成。
public class SampleAsyncActionFilter: IAsyncActionFilter{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){//操作執(zhí)行前做的事情await next();//操作執(zhí)行后做的事情 }}
3.過(guò)濾器作用域
過(guò)濾器有三種不同級(jí)別的作用域。你可以在特定的操作上用特性(Attribute)的方式使用特定的過(guò)濾器。也可以在控制器上用特性的方式使用過(guò)濾器,這樣就可以將效果作用在控制器內(nèi)的所有操作上。或者注冊(cè)一個(gè)全局過(guò)濾器,它將作用于整個(gè) MVC 應(yīng)用程序的每一個(gè)操作。
如果想要使用全局過(guò)濾器,可以在配置 MVC 時(shí),在 Startup 的 ConfigureServices 方法中添加:
services.AddMvc(options =>{options.Filters.Add(typeof(SampleActionFilter));//通過(guò)類(lèi)型options.Filters.Add(new SampleActionFilter());//注冊(cè)實(shí)例}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);過(guò)濾器既可以通過(guò)類(lèi)型添加,也可以通過(guò)實(shí)例添加。如果通過(guò)實(shí)例添加,則該實(shí)例會(huì)被使用于每一個(gè)請(qǐng)求。如果通過(guò)類(lèi)型添加,則在每次請(qǐng)求后都會(huì)創(chuàng)建一個(gè)實(shí)例,其所有構(gòu)造函數(shù)依賴(lài)項(xiàng)都將通過(guò) DI 來(lái)填充。
把過(guò)濾器接口的實(shí)現(xiàn)當(dāng)作特性使用也非常方便。過(guò)濾器特性可應(yīng)用于控制器和操作方法。框架包含了內(nèi)置的基于特性的過(guò)濾器,可以繼承他們或者另外定制。例如,下面的過(guò)濾器繼承了 ResultFilterAttribute,并重寫(xiě) OnResultExecuting 方法(在響應(yīng)中增加一個(gè)信息頭):
public class AddHeaderAttribute: ResultFilterAttribute{private readonly string _name;private readonly string _value;public AddHeaderAttribute(string name, string value){_name = name;_value = value;}public override void OnResultExecuting(ResultExecutingContext context){context.HttpContext.Response.Headers.Add(_name,new string[] { _value });base.OnResultExecuting(context);}}
特性允許過(guò)濾器接受參數(shù),如下,可將此特性添加到控制器或操作中,并為其指定所需 HTTP 頭的名稱(chēng)和值:
[AddHeader("Author", "Ruby Lu")]public class HomeController : Controller { }以下幾種過(guò)濾器接口可以自定義為相應(yīng)特性的實(shí)現(xiàn):
ActionFilterAttribute
ExceptionFilterAttribute
ResultFilterAttribute
FormatFilterAttribute
ServiceFilterAttribute
TypeFilterAttribute
?
4.取消和短路
通過(guò)設(shè)置傳入過(guò)濾器方法的上下文參數(shù)中的 Result 屬性,可以在過(guò)濾器管道的任意一點(diǎn)短路管道。比如,下面的 ShortCircuitingResourceFilter 將阻止它之后管道內(nèi)的所有過(guò)濾器,包括所有操作過(guò)濾器:
public class ShortCircuitingResourceFilter:Attribute,IResourceFilter{public void OnResourceExecuting(ResourceExecutingContext context){context.Result = new ContentResult() {Content = "短路"};}public void OnResourceExecuted(ResourceExecutedContext context){}}?
2.配置過(guò)濾器
全局過(guò)濾器在 Startup 中配置。基于特性的過(guò)濾器如果不需要任何依賴(lài),可以簡(jiǎn)單地繼承一個(gè)已存在地過(guò)濾器相對(duì)應(yīng)地特性類(lèi)型。如果要?jiǎng)?chuàng)建一個(gè)非全局作用域,但需要從依賴(lài)注入中獲得依賴(lài)項(xiàng)的過(guò)濾器,那么在它們上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,這樣就可用于控制器或操作了。
1.依賴(lài)注入
以特性形式實(shí)現(xiàn)的,直接添加到控制器或操作的過(guò)濾器,其構(gòu)造函數(shù)不得由依賴(lài)注入提供依賴(lài)項(xiàng)。其原因在于,特性所需的構(gòu)造函數(shù)參數(shù)必須由使用處直接提供。這是特性原型機(jī)理的限制。
如果過(guò)濾器需要從 DI 中獲得依賴(lài)項(xiàng),那么可以用以下幾種方法在類(lèi)或操作方法使用:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory 實(shí)現(xiàn)特性
TypeFilter 將為其依賴(lài)項(xiàng)從 DI 中使用服務(wù)來(lái)實(shí)例化一個(gè)實(shí)例。 ServiceFilter 則從 DI 中獲取一個(gè)過(guò)濾器實(shí)例。下面演示 ServiceFilter:
先在 ConfigureServices 中注冊(cè)?AddHeaderFilterWithDI 類(lèi)型:services.AddScoped<AddHeaderFilterWithDI>();?
然后使用:
[ServiceFilter(typeof(AddHeaderFilterWithDI))]
public IActionResult Index()
{
}
ServiceFilterAttribute 實(shí)現(xiàn)了IFilterFactory 接口,它公開(kāi)了一個(gè)創(chuàng)建 IFilter 實(shí)例的方法。在?ServiceFilterAttribute 中,IFilterFactory 接口的 CreateInstance 方法被實(shí)現(xiàn)為從服務(wù)容器加載指定的類(lèi)型。
TypeFilterAttribute 非常類(lèi)似?ServiceFilterAttribute (也實(shí)現(xiàn) IFilterFactory 接口),但它的類(lèi)型不是直接從 DI 容器中解析,相反,它使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 實(shí)例化類(lèi)型。
由于這種差異,使用?TypeFilterAttribute 引用的類(lèi)型不需要在使用前向容器注冊(cè),但它們?nèi)杂扇萜鱽?lái)填充其依賴(lài)項(xiàng)。此外,TypeFilterAttribute 可以可選的接受該類(lèi)型的構(gòu)造函數(shù)參數(shù)。下面是?TypeFilterAttribute 演示:
[TypeFilter(typeof(AddHeaderAttribute),Arguments =new object[] { "Author","Ruby" })]public IActionResult Index(){return View();}? 如果有一個(gè)簡(jiǎn)單的過(guò)濾器,不需要任何參數(shù),但有構(gòu)造函數(shù)需要通過(guò) DI 填充依賴(lài)項(xiàng),那么可以繼承?TypeFilterAttribute,允許使用自己命名的特性類(lèi)和方法(而不是 [TypeFilterAttribute(typeof(FilterType))])。下面的過(guò)濾器顯示了如何實(shí)現(xiàn)此功能:
public class SampleActionFilterAttribute:TypeFilterAttribute{public SampleActionFilterAttribute() : base(typeof(SampleActionFilterImpl)){}private class SampleActionFilterImpl:IActionFilter{public void OnActionExecuting(ActionExecutingContext context){//操作執(zhí)行前做的事情 }public void OnActionExecuted(ActionExecutedContext context){//操作執(zhí)行后做的事情 }}}該過(guò)濾器可通過(guò)使用 [SampleActionFilter] 這樣的語(yǔ)法應(yīng)用于類(lèi)或方法,而不必使用 [TypeFilter] 或 [ServiceFilter] 。
IFilterFactory 實(shí)現(xiàn) IFilter ,因此在過(guò)濾器管道中,任何位置的??IFilterFactory 實(shí)例都可當(dāng)作 Filter 實(shí)例來(lái)使用。當(dāng)框架準(zhǔn)備調(diào)用過(guò)濾器時(shí),將嘗試將其轉(zhuǎn)換為?IFilterFactory 。如果轉(zhuǎn)換成功, 則調(diào)用 CreateInstance 方法來(lái)創(chuàng)建將被調(diào)用的 IFilter 實(shí)例。這是一種非常靈活的設(shè)計(jì),因?yàn)楫?dāng)應(yīng)用程序啟動(dòng)時(shí),不需要明確地設(shè)置精確地過(guò)濾器。
你可以在自己地特性中實(shí)現(xiàn)?IFilterFactory 幾口,作為另一種創(chuàng)建過(guò)濾器的方法:
public class AddHeadWithFactoryAttribute:Attribute, IFilterFactory{public bool IsReusable { get; }//實(shí)現(xiàn)IFilterFactorypublic IFilterMetadata CreateInstance(IServiceProvider serviceProvider){return new InternalAddHeaderFilter();}}public class InternalAddHeaderFilter : IResultFilter{public void OnResultExecuting(ResultExecutingContext context){context.HttpContext.Response.Headers.Add("Internal", new string[] { "Header Add" });}public void OnResultExecuted(ResultExecutedContext context){}}?
2.排序
? 過(guò)濾器可以應(yīng)用于操作方法或控制器(通過(guò)特性)或添加到全局過(guò)濾器集合中。作用域通常也決定了排序,最接近操作的過(guò)濾器首先運(yùn)行。
除了作用域,過(guò)濾器還可以通過(guò)實(shí)現(xiàn) IOrderedFilter 來(lái)重寫(xiě)它們的執(zhí)行順序。此接口簡(jiǎn)單的暴露了一個(gè) int Order 屬性,并且過(guò)濾器基于該屬性以數(shù)字升序執(zhí)行。所有內(nèi)置的過(guò)濾器,包括?TypeFilterAttribute 和?ServiceFilterAttribute ,都實(shí)現(xiàn)?IOrderedFilter? 接口。,因此當(dāng)將過(guò)濾器特性應(yīng)用于類(lèi)或方法時(shí),可以指定過(guò)濾器執(zhí)行順序。默認(rèn)情況下,所有內(nèi)置過(guò)濾器的 Order 屬性都為0,因此范圍用作分隔符,并且是決定性因素(除非 Order 設(shè)置為 0)。
每個(gè)從 Controller 基類(lèi)繼承的控制器都包含?OnActionExecuting 和?OnActionExecuted 方法。這些方法為給定操作包裝了過(guò)濾器,它們分別最先運(yùn)行和最后運(yùn)行。假設(shè)沒(méi)有為任何過(guò)濾器設(shè)置 Order 舒總,那么單純基于范圍的順序?yàn)?#xff1a;
控制器的?OnActionExecuting
全局過(guò)濾器的OnActionExecuting
類(lèi)過(guò)濾器的OnActionExecuting
方法過(guò)濾器的OnActionExecuting
方法過(guò)濾器的OnActionExecuted?
類(lèi)過(guò)濾器的OnActionExecuted?
全局過(guò)濾器的OnActionExecuted?
控制器過(guò)濾器的OnActionExecuted?
?
要修改默認(rèn)的基于范圍的順序,則應(yīng)顯示設(shè)置類(lèi)級(jí)別或者方法級(jí)別過(guò)濾器的 Order 屬性。例如,將 Order = -1 添加到方法級(jí)屬性:
[MyFilter (Name = "...",Order = -1)]
在這種情況下,小于零的值將確保此過(guò)濾器在全局和類(lèi)級(jí)過(guò)濾器之前運(yùn)行:
控制器的?OnActionExecuting
方法過(guò)濾器的OnActionExecuting
全局過(guò)濾器的OnActionExecuting
類(lèi)過(guò)濾器的OnActionExecuting
類(lèi)過(guò)濾器的OnActionExecuted?
全局過(guò)濾器的OnActionExecuted?
控制器過(guò)濾器的OnActionExecuted?
方法過(guò)濾器的OnActionExecuted?
Controller 類(lèi)的方法總是在所有過(guò)濾器之前和之后運(yùn)行。這些方法不作為IFilter實(shí)例實(shí)現(xiàn)。也不參與IFilter排序算法。
?
3.對(duì)比中間件
一般來(lái)說(shuō),過(guò)濾器用于處理業(yè)務(wù)與應(yīng)用程序的橫切關(guān)注點(diǎn),用法和功能很像中間件,但過(guò)濾器允許你將作用范圍縮小,并將其插入到應(yīng)用程序中有意義的位置,例如視圖之前或模型綁定之后。過(guò)濾器是 MVC 的一部分,可以訪問(wèn)其上下文和構(gòu)造函數(shù)。例如,中間件很難檢測(cè)到請(qǐng)求的模型驗(yàn)證是否產(chǎn)生錯(cuò)誤,并且做出相應(yīng)的響應(yīng)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/afei-24/p/11334100.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core MVC 之过滤器(Filter)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 移动互联时代,域名还有价值吗?
- 下一篇: 推特安全设置(更改推特安全设置)