ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器
原文:Filters
作者:Steve Smith
翻譯:劉怡(AlexLEWIS)
校對:何鎮(zhèn)汐
ASP.NET MVC 過濾器 可在執(zhí)行管道的前后特定階段執(zhí)行代碼。過濾器可以配置為全局有效、僅對控制器有效或是僅對 Action 有效。
查看或下載演示代碼.
過濾器如何工作?
不同的過濾器類型會(huì)在執(zhí)行管道的不同階段運(yùn)行,因此它們各自有一套適用場景。根據(jù)你實(shí)際要解決的問題以及在請求管道中執(zhí)行的位置來選擇創(chuàng)建不同的過濾器。運(yùn)行于 MVC Action 調(diào)用管道內(nèi)的過濾器有時(shí)被稱為 過濾管道 ,當(dāng) MVC 選擇要執(zhí)行哪個(gè) Action 后就會(huì)先執(zhí)行該 Action 上的過濾器。
不同過濾器在管道的不同位置運(yùn)行。像授權(quán)這樣的過濾器只運(yùn)行在管道的靠前位置,并且其后也不會(huì)跟隨 action。其它過濾器(如 action 過濾器等)可以在管道的其它部分之前或之后執(zhí)行,如下所示。
選擇過濾器
授權(quán)過濾器 用于確定當(dāng)前用戶的請求是否合法。
資源過濾器 是授權(quán)之后第一個(gè)用來處理請求的過濾器,也是最后一個(gè)接觸到請求的過濾器(因?yàn)橹缶蜁?huì)離開過濾器管道)。在性能方面,資源過濾器在實(shí)現(xiàn)緩存或短路過濾器管道尤其有用。
Action 過濾器 包裝了對單個(gè) action 方法的調(diào)用,可以將參數(shù)傳遞給 action 并從中獲得 action result。
異常過濾器 為 MVC 應(yīng)用程序未處理異常應(yīng)用全局策略。
結(jié)果過濾器 包裝了單個(gè) action result 的執(zhí)行,當(dāng)且僅當(dāng) action 方法成功執(zhí)行完畢后方才運(yùn)行。它們是理想的圍繞視圖執(zhí)行或格式處理的邏輯(所在之處)。
實(shí)現(xiàn)
所有過濾器均可通過不同的接口定義來支持同步和異步實(shí)現(xiàn)。根據(jù)你所需執(zhí)行的任務(wù)的不同來選擇同步還是異步實(shí)現(xiàn)。從框架的角度來講它們是可以互換的。
同步過濾器定義了 OnStageExecuting 方法和 OnStageExecuted 方法(當(dāng)然也有例外)。OnStageExecuting 方法在具體事件管道階段之前調(diào)用,而 OnStageExecuted 方法則在之后調(diào)用(比如當(dāng) Stage 是 Action 時(shí),這兩個(gè)方法便是 OnActionExecuting 和 OnActionExecuted,譯者注)。
異步過濾器定義了一個(gè) On?Stage?ExecutionAsync 方法,可以在具體管道階段的前后運(yùn)行。On?Stage?ExecutionAsync 方法被提供了一個(gè) Stage?ExecutionDelegate 委托,當(dāng)調(diào)用時(shí)該委托會(huì)執(zhí)行具體管道階段的工作,然后等待其完成。
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters;namespace FiltersSample.Filters {public class SampleAsyncActionFilter : IAsyncActionFilter{public async Task OnActionExecutionAsync(ActionExecutingContext context,ActionExecutionDelegate next){// do something before the action executesawait next();// do something after the action executes}} }注解
只能 實(shí)現(xiàn)一個(gè)過濾器接口,要么是同步版本的,要么是異步版本的,魚和熊掌不可兼得 。如果你需要在接口中執(zhí)行異步工作,那么就去實(shí)現(xiàn)異步接口。否則應(yīng)該實(shí)現(xiàn)同步版本的接口??蚣軙?huì)首先檢查是不是實(shí)現(xiàn)了異步接口,如果實(shí)現(xiàn)了異步接口,那么將調(diào)用它。不然則調(diào)用同步接口的方法。如果一個(gè)類中實(shí)現(xiàn)了兩個(gè)接口,那么只有異步方法會(huì)被調(diào)用。最后,不管 action 是同步的還是異步的,過濾器的同步或是異步是獨(dú)立于 action 的。
過濾器作用域
過濾器具有三種不同級(jí)別的 作用域 。你可以在特定的 action 上用特性(Attribute)的方式使用特定的過濾器;也可以在控制器上用特性的方式使用過濾器,這樣就會(huì)將效果應(yīng)用在控制器內(nèi)所有的 action 上;或者注冊一個(gè)全局過濾器,它將作用于整個(gè) MVC 應(yīng)用程序下的每一個(gè) action。
如果想要使用全局過濾器的話,在你配置 MVC 的時(shí)候在 Startup 的 ConfigureServices 方法中添加:
public void ConfigureServices(IServiceCollection services) {services.AddMvc(options =>{options.Filters.Add(typeof(SampleActionFilter)); // by typeoptions.Filters.Add(new SampleGlobalActionFilter()); // an instance});services.AddScoped<AddHeaderFilterWithDi>(); }過濾器可通過類型添加,也可以通過實(shí)例添加。如果通過實(shí)例添加,則該實(shí)例會(huì)被用于每一個(gè)請求。如果通過類型添加,則將會(huì) type-activated(意思是說每次請求都會(huì)創(chuàng)建一個(gè)實(shí)例,其所有構(gòu)造函數(shù)依賴項(xiàng)都將通過 DI 來填充)。通過類型添加過濾器相當(dāng)于 filters.Add(new TypeFilterAttribute(typeof(MyFilter))) 。
把過濾器接口的實(shí)現(xiàn)當(dāng)做 特性(Attributes) 使用是極為方便的。過濾器特性(filter attributes)可應(yīng)用于控制器(Controllers)和 Action 方法??蚣馨藘?nèi)置的基于特性的過濾器,你可繼承它們或另外定制。比方說,下例過濾器繼承了 ResultFilterAttribute ,并重寫(override)了 OnResultExecuting 方法(在響應(yīng)中增加了一個(gè)頭信息)。
using Microsoft.AspNetCore.Mvc.Filters;namespace FiltersSample.Filters {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);}} }特性允許過濾器接收參數(shù),如下例所示。可將此特性加諸控制器(Controller)或 Action 方法,并為其指定所需 HTTP 頭的名稱和值,并將該 HTTP 頭加入響應(yīng)中:
[AddHeader("Author", "Steve Smith @ardalis")] public class SampleController : Controller {public IActionResult Index(){return Content("Examine the headers using developer tools.");} }Index Action 的結(jié)果如下所示:響應(yīng)的頭信息顯示在右下角。
以下幾種過濾器接口可以自定義為相應(yīng)特性的實(shí)現(xiàn)。
過濾器特性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
取消與短路
通過設(shè)置傳入過濾器方法的上下文參數(shù)中的 Result 屬性,可以在過濾器管道的任意一點(diǎn)短路管道。比方說,下面的 ShortCircuitingResourceFilter 將阻止所有它之后管道內(nèi)的所有過濾器,包括所有 action 過濾器。
using System; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters;namespace FiltersSample.Filters {public class ShortCircuitingResourceFilterAttribute : Attribute,IResourceFilter{public void OnResourceExecuting(ResourceExecutingContext context){context.Result = new ContentResult(){Content = "Resource unavailable - header should not be set"};}public void OnResourceExecuted(ResourceExecutedContext context){}} }在下例中,ShortCircuitingResourceFilter 和 AddHeader 過濾器都指向了名為 SomeResource 的 action 方法。然而,由于首先運(yùn)行的是 ShortCircuitingResourceFilter ,它短路了剩下的管道,SomeResource 上的 AddHeader 過濾器不會(huì)運(yùn)行。如果這兩個(gè)過濾器都以 Action 方法級(jí)別出現(xiàn),它們的結(jié)果也會(huì)是一樣的(只要 ShortCircuitingResourceFilter 首先運(yùn)行,請查看 Ordering )。
[AddHeader("Author", "Steve Smith @ardalis")] public class SampleController : Controller {[ShortCircuitingResourceFilter]public IActionResult SomeResource(){return Content("Successful access to resource - header should be set.");}配置過濾器
全局過濾器在 Startup.cs 中配置。基于特性的過濾器如果不需要任何依賴項(xiàng)的話,可以簡單地繼承一個(gè)與已存在的過濾器相對應(yīng)的特性類型。如果要?jiǎng)?chuàng)建一個(gè) 非 全局作用域、但需要從依賴注入(DI)中獲得依賴項(xiàng)的過濾器,在它們上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,這樣就可用于控制器或 action 了。
依賴注入
以特性形式實(shí)現(xiàn)的、直接添加到控制器(Controller)類或 Action 方法的過濾器,其構(gòu)造函數(shù)不得由 依賴注入 (DI)提供依賴項(xiàng)。其原因在于特性所需的構(gòu)造函數(shù)參數(shù)必須由使用處直接提供。這是特性原型機(jī)理的限制。
不過,如果過濾器需要從 DI 中獲得依賴項(xiàng),那么有幾種辦法可以實(shí)現(xiàn),可在類(class)或 Action 方法使用:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory 實(shí)現(xiàn)你的特性
TypeFilter 將為其依賴項(xiàng)從 DI 中使用服務(wù)(services)來實(shí)例化一個(gè)實(shí)例。ServiceFilter 則從 DI 中取回一個(gè)過濾器實(shí)例。下例中將演示如何使用 ServiceFilter:
[ServiceFilter(typeof(AddHeaderFilterWithDi))] public IActionResult Index() {return View(); }如果在 ConfigureServices 中直接使用未經(jīng)注冊的 ServiceFilter 過濾器,則會(huì)拋出以下異常:
System.InvalidOperationException: No service for type 'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.為避免此異常,你必須在 ConfigureServices 中為 AddHeaderFilterWithDI 類型注冊:
services.AddScoped<AddHeaderFilterWithDi>();ServiceFilterAttribute 實(shí)現(xiàn)了 IFilterFactory 接口,后者暴露了創(chuàng)建 IFilter 實(shí)例的單一方法。在 ServiceFilterAttribute 中,接口 IFilterFactory 中定義的 CreateInstance 方法被實(shí)現(xiàn)為用于從服務(wù)容器(DI)加載指定類型。
TypeFilterAttribute 很像 ServiceFilterAttribute (它同樣是 IFilterFactory 的實(shí)現(xiàn)),但此類型并非直接解析自 DI 容器。
相反,它通過使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來實(shí)例化類型。
由于這種不同,使用 TypeFilterAttribute 引用的類型不需要在使用之前向容器注冊(但它們依舊將由容器來填充其依賴項(xiàng))。同樣地,TypeFilterAttribute 能可選地接受該類型的構(gòu)造函數(shù)參數(shù)。下例演示如何向使用 TypeFilterAttribute 修飾的類型傳遞參數(shù):
[TypeFilter(typeof(AddHeaderAttribute),Arguments = new object[] { "Author", "Steve Smith (@ardalis)" })] public IActionResult Hi(string name) {return Content($"Hi {name}"); }若是你有一個(gè)簡單的不需要任何參數(shù)的、但其構(gòu)造函數(shù)需要通過 DI 填充依賴項(xiàng)的過濾器的話,你可以通過繼承 TypeFilterAttribute,在類(class)或方法(method)上使用自己命名的特性(來取代 [TypeFilter(typeof(FilterType))])。下例過濾器向你展示這是如何實(shí)現(xiàn)的:
public class SampleActionFilterAttribute : TypeFilterAttribute {public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl)){}private class SampleActionFilterImpl : IActionFilter{private readonly ILogger _logger;public SampleActionFilterImpl(ILoggerFactory loggerFactory){_logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();}public void OnActionExecuting(ActionExecutingContext context){_logger.LogInformation("Business action starting...");// perform some business logic work}public void OnActionExecuted(ActionExecutedContext context){// perform some business logic work_logger.LogInformation("Business action completed.");}} }該過濾器可通過使用 [SampleActionFilter] 這樣的語法應(yīng)用于類或方法,而不必使用 [TypeFilter] 或 [ServiceFilter]。
注解
應(yīng)避免純粹為記錄日志而創(chuàng)建和使用過濾器,這是因?yàn)?內(nèi)建的框架日志功能 應(yīng)該已經(jīng)提供了你所需的功能。如果你要把日志記錄功能放入過濾器中,它應(yīng)專注于業(yè)務(wù)領(lǐng)域或過濾器的具體行為,而不是 MVC Action 或框架事件。
IFilterFactory 實(shí)現(xiàn)了 IFilter。因此,在過濾器管道的任何地方 IFilterFactory 實(shí)例都可當(dāng)做 IFilter 實(shí)例來使用。當(dāng)框架準(zhǔn)備調(diào)用過濾器,將試圖把其強(qiáng)制轉(zhuǎn)換為 IFilterFactory。如果轉(zhuǎn)換成功,將通過調(diào)用 CreateInstance 方法創(chuàng)建即將被調(diào)用的 IFilter 實(shí)例。因?yàn)檫^濾器管道不需要在應(yīng)用程序啟動(dòng)時(shí)顯式設(shè)置了,所以這是一種非常靈活的設(shè)計(jì)。
你可以在自己的特性實(shí)現(xiàn)中實(shí)現(xiàn) IFilterFactory 接口,以此來實(shí)現(xiàn)另一種創(chuàng)建過濾器的方法:
public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory {// Implement IFilterFactorypublic IFilterMetadata CreateInstance(IServiceProvider serviceProvider){return new InternalAddHeaderFilter();}private class InternalAddHeaderFilter : IResultFilter{public void OnResultExecuting(ResultExecutingContext context){context.HttpContext.Response.Headers.Add("Internal", new string[] { "Header Added" });}public void OnResultExecuted(ResultExecutedContext context){}}排序
過濾器可應(yīng)用于 Action 方法、控制器(Controller,通過特性(attribute)的形式)或添加到全局過濾器集合中。其作用域通常還決定了其執(zhí)行順序。最靠近 Action 的過濾器首先執(zhí)行;通常來講通過重寫行為而不是顯式設(shè)置順序來改變順序。這有時(shí)被稱為“俄羅斯套娃”,因?yàn)槊恳粋€(gè)作用范圍都包裹了前一個(gè)作用范圍,就像是 套娃 那般。
除了作用范圍,過濾器還可以通過實(shí)現(xiàn) IOrderedFilter 來重寫它們的執(zhí)行順序。該接口只是簡單地暴露了 int Order 屬性,然后執(zhí)行時(shí)根據(jù)該數(shù)字正排序(數(shù)字越小,越先執(zhí)行)后依次執(zhí)行過濾器。所有內(nèi)建過濾器(包括 TypeFilterAttribute 和 ServiceFilterAttribute)都實(shí)現(xiàn)了 IOrderedFilter 接口,因此當(dāng)你將過濾器以特性的方式用于類(class)或方法(method)時(shí)你可以指定每一個(gè)過濾器的執(zhí)行順序。默認(rèn)情況下所有內(nèi)建過濾器的 Order 屬性值都為 0,因此作用范圍就當(dāng)做決定性的因素(除非存在不為 0 的 Order 值)。
每個(gè)繼承自 Controller 基類的控制器(Controller)都包含 OnActionExecuting 和 OnActionExecuted 方法。這些方法為給定的 Action 包裝了過濾器,它們分別在最先和最后運(yùn)行?;谧饔梅秶捻樞?#xff08;假設(shè)沒有為過濾器的 Order 設(shè)置任何值):
注解
當(dāng)過濾器將啟動(dòng)運(yùn)行、需要決定過濾器執(zhí)行順序時(shí),IOrderedFilter 會(huì)向外宣布自己的作用范圍。過濾器首先通過 order 來排序,然后通過作用范圍來決定。如果不設(shè)置,則 Order 默認(rèn)為 0。
為在基于作用范圍的排序中修改默認(rèn)值,你須在類一級(jí)(class-level)或方法一級(jí)(method-level)的過濾器上顯式設(shè)置 Order 屬性。比如為方法一級(jí)的特性增加 Order=-1:
[MyFilter(Name = "Method Level Attribute", Order=-1)]這種情況下,小于 0 的值將確保該過濾器在全局過濾器和類一級(jí)過濾器之前運(yùn)行(假設(shè)它們的 Order 屬性均未設(shè)置)。
新的排序可能是這樣的:
注解
Controller 類的方法總是在所有過濾器之前和之后運(yùn)行。這些方法并未實(shí)現(xiàn)為 IFilter 實(shí)現(xiàn),同時(shí)它們不參與 IFilter 的排序算法。
授權(quán)過濾器
授權(quán)過濾器 控制對 action 方法的訪問,也是過濾器管道中第一個(gè)被執(zhí)行的過濾器。它們只有一個(gè)前置階段,不像其它大多數(shù)過濾器支持前置階段方法和后置階段方法。只有當(dāng)你使用自己的授權(quán)框架時(shí)才需要定制授權(quán)過濾器。謹(jǐn)記勿在授權(quán)過濾器內(nèi)拋出異常,這是因?yàn)樗鶔伋龅漠惓2粫?huì)被處理(異常過濾器也不會(huì)處理它們)。此時(shí)記錄該問題或?qū)で笃渌k法。
更多請?jiān)L問 Authorization
資源過濾器
資源過濾器 要么實(shí)現(xiàn) IResourceFilter 接口,要么實(shí)現(xiàn) IAsyncResourceFilter 接口,它們執(zhí)行于大多數(shù)過濾器管道(只有 授權(quán)過濾器 在其之前運(yùn)行,其余所有過濾器以及 Action 處理均出現(xiàn)在其 OnResourceExecuting 和 OnResourceExecuted 方法之間)。當(dāng)你需要短路絕大多數(shù)正在進(jìn)行的請求時(shí),資源過濾器特別有用。資源過濾器的一個(gè)典型例子是緩存,如果響應(yīng)已經(jīng)被緩存,過濾器會(huì)立即將之置為結(jié)果以避免后續(xù) Action 的多余操作過程。
上面所說的是一個(gè) 短路資源過濾器 的例子。下例是一個(gè)非常簡單的緩存實(shí)現(xiàn)(請勿將之用于生產(chǎn)環(huán)境),只能與 ContentResult 配合使用,如下所示:
public class NaiveCacheResourceFilterAttribute : Attribute,IResourceFilter {private static readonly Dictionary<string, object> _cache= new Dictionary<string, object>();private string _cacheKey;public void OnResourceExecuting(ResourceExecutingContext context){_cacheKey = context.HttpContext.Request.Path.ToString();if (_cache.ContainsKey(_cacheKey)){var cachedValue = _cache[_cacheKey] as string;if (cachedValue != null){context.Result = new ContentResult(){ Content = cachedValue };}}}public void OnResourceExecuted(ResourceExecutedContext context){if (!String.IsNullOrEmpty(_cacheKey) &&!_cache.ContainsKey(_cacheKey)){var result = context.Result as ContentResult;if (result != null){_cache.Add(_cacheKey, result.Content);}}} }在 OnResourceExecuting 中,如果結(jié)果已經(jīng)在靜態(tài)字段緩存中,Result 屬性將被設(shè)置到 context 上,同時(shí) Action 被短路并返回緩存的結(jié)果。在 OnResourceExecuted 方法中,如果當(dāng)前其請求的鍵未被使用過,那么 Result 就會(huì)被保存到緩存中,用于之后的請求。
如下所示,把這個(gè)過濾器用于類或方法之上:
[TypeFilter(typeof(NaiveCacheResourceFilterAttribute))] public class CachedController : Controller {public IActionResult Index(){return Content("This content was generated at " + DateTime.Now);} }Action 過濾器
Action 過濾器 要么實(shí)現(xiàn) IActionFilter 接口,要么實(shí)現(xiàn) IAsyncActionFilter 接口,它們可以在 action 方法執(zhí)行的前后被執(zhí)行。Action 過濾器非常適合放置諸如查看模型綁定結(jié)果、或是修改控制器或輸入到 action 方法的邏輯。另外,action 過濾器可以查看并直接修改 action 方法的結(jié)果。
OnActionExecuting 方法在 action 方法執(zhí)行之前運(yùn)行,因此它可以通過改變 ActionExecutingContext.ActionArguments 來控制 action 的輸入,或是通過 ActionExecutingContext.Controller 控制控制器(Controller)。OnActionExecuting 方法可以通過設(shè)置 ActionExecutingContext.Result 來短路 action 方法的操作及其后續(xù)的過濾器。OnActionExecuting 方法通過拋出異常也可以阻止 action 方法和后續(xù)過濾器的處理,但會(huì)當(dāng)做失敗(而不是成功)的結(jié)果來處理。
OnActionExecuted 方法在 action 方法執(zhí)行之后才執(zhí)行,并且可以通過 ActionExecutedContext.Result 屬性查看或控制 action 的結(jié)果。如果 action 在執(zhí)行時(shí)被其它過濾器短路,則 ActionExecutedContext.Canceled 將會(huì)被置為 true。如果 action 或后續(xù)的 action 過濾器拋出異常,則 ActionExecutedContext.Exception 會(huì)被設(shè)置為一個(gè)非空值。有效「處理」完異常后把 ActionExecutedContext.Exception 設(shè)置為 null,那么 ActionExectedContext.Result 會(huì)像從 action 方法正常返回值那樣被處理。
對于 IAsyncActionFilter 接口來說,它的 OnActionExecutionAsync 方法結(jié)合了 OnActionExecuting 和 OnActionExecuted 的所有能力。調(diào)用 await next() 后,ActionExecutionDelegate 將會(huì)執(zhí)行所有的后續(xù) action 過濾器以及 action 方法,并返回 ActionExecutedContext 。
如果想要在 OnActionExecutionAsync 內(nèi)部短路,那么就為 ActionExecutingContext.Result 分配一個(gè)結(jié)果實(shí)例,并且不要調(diào)用 ActionExecutionDelegate 即可。
異常過濾器
異常過濾器 實(shí)現(xiàn)了 IExceptionFilter 接口或 IAsyncExceptionFilter 接口。
異常過濾器用于處理「未處理異常」,包括發(fā)生在 Controller 創(chuàng)建及 模型綁定 期間出現(xiàn)的異常。它們只在管道內(nèi)發(fā)生異常時(shí)才會(huì)被調(diào)用。它們提供了一個(gè)單一的位置實(shí)現(xiàn)應(yīng)用程序內(nèi)的公共異常處理策略。框架提供了抽象的 ExceptionFilterAttribute ,你根據(jù)自己的需要繼承這個(gè)類。異常過濾器適用于捕獲 MVC Action 內(nèi)出現(xiàn)的異常,但它們不及錯(cuò)誤處理中間件(error handling middleware)靈活。一般來講優(yōu)先使用中間件,只有在需要做一些基于所選 MVC Action 的、有別于錯(cuò)誤處理的工作時(shí)才選擇使用過濾器。
提示
對于應(yīng)用程序中不同 action 需要使用不同的錯(cuò)誤處理方式,并向 Views/HTML 暴露 API 端點(diǎn)或 action 的錯(cuò)誤處理結(jié)果。API 端點(diǎn)用 JSON 返回錯(cuò)誤信息,而基于視圖的 action 則返回錯(cuò)誤頁面(HTML 頁面)。
異常過濾器不應(yīng)有兩個(gè)事件(對于前置或后置而言),它們只實(shí)現(xiàn) OnException (或 OnExceptionAsync )。以參數(shù)形式傳入 OnException 的 ExceptionContext 包含了所發(fā)生的 Exception。如果把 context.Exception 設(shè)置為 null,其效果相當(dāng)于你已處理該異常,所以該次請求會(huì)像沒發(fā)生過異常那樣繼續(xù)處理(一般會(huì)返回 HTTP 200 OK 狀態(tài))。下例過濾器中使用定制的開發(fā)者錯(cuò)誤視圖來顯示開發(fā)環(huán)境中應(yīng)用程序所出現(xiàn)異常的詳細(xì)信息:
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ViewFeatures;namespace FiltersSample.Filters {public class CustomExceptionFilterAttribute : ExceptionFilterAttribute{private readonly IHostingEnvironment _hostingEnvironment;private readonly IModelMetadataProvider _modelMetadataProvider;public CustomExceptionFilterAttribute(IHostingEnvironment hostingEnvironment,IModelMetadataProvider modelMetadataProvider){_hostingEnvironment = hostingEnvironment;_modelMetadataProvider = modelMetadataProvider;}public override void OnException(ExceptionContext context){if (!_hostingEnvironment.IsDevelopment()){// do nothingreturn;}var result = new ViewResult {ViewName = "CustomError"};result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState);result.ViewData.Add("Exception", context.Exception);// TODO: Pass additional detailed data via ViewDatacontext.ExceptionHandled = true; // mark exception as handledcontext.Result = result;}} }結(jié)果過濾器
實(shí)現(xiàn)了 IResultFilter 或 IAsyncResultFilter 接口的 結(jié)果過濾器 在 Action Result 執(zhí)行體的周圍執(zhí)行。當(dāng) Action 或 Action 過濾器產(chǎn)生 Action 結(jié)果時(shí),只有成功運(yùn)行的才會(huì)執(zhí)行結(jié)果過濾器。如果異常過濾器處理了異常,那么結(jié)果過濾器就不會(huì)運(yùn)行——除非異常過濾器將異常設(shè)置為null(Exception = null)。
注解
正在執(zhí)行的結(jié)果種類取決于相關(guān) Action。MVC Action 所返回的 View 將包含 Razor(將其作為正在處理的 ViewResult 的一部分)。API 方法則將執(zhí)行一些序列化工作作為其執(zhí)行結(jié)果的一部分。了解更多請移步 action 結(jié)果。
結(jié)果過濾器適用于任何需要直接環(huán)繞 View 或格式化處理的邏輯。結(jié)果過濾器可以替換或更改 Action 結(jié)果(而后者負(fù)責(zé)產(chǎn)生響應(yīng))。
OnResultExecuting 方法運(yùn)行于 Action 結(jié)果執(zhí)行之前,故其可通過 ResultExecutingContext.Result 操作 Action 結(jié)果。如果將 ResultExecutingContext.Cancel 設(shè)置為 true,則 OnResultExecuting 方法可短路 Action 結(jié)果以及后續(xù)結(jié)果過濾器的執(zhí)行。如果發(fā)生了短路,MVC 將不會(huì)修改響應(yīng),所以當(dāng)發(fā)生短路時(shí),為避免生成空響應(yīng),你一般應(yīng)該直接去修改響應(yīng)對象。如果在 OnResultExecuting 方法內(nèi)拋出異常,那么也將阻止 Action 結(jié)果以及后續(xù)過濾器的執(zhí)行,但會(huì)被當(dāng)做失敗結(jié)果(而非成功結(jié)果)。
OnResultExecuted 方法運(yùn)行于 Action 結(jié)果執(zhí)行之后。也就是說,如果沒有拋出異常,響應(yīng)可能就會(huì)被發(fā)送到客戶端且不可再修改。如果 Action 結(jié)果在執(zhí)行中被其它過濾器短路,則 ResultExecutedContext.Canceled 將被置為 true。如果 Action 結(jié)果或后續(xù)結(jié)果過濾器拋出異常,則 ResultExecutedContext.Exception 將被置為非空值(non-null value)。把 ResultExecutedContext.Exception 設(shè)置為 null 后會(huì)影響到異常的“處理”,這將阻止異常在之后的管道內(nèi)被 MVC 重新拋出。如果在結(jié)果過濾器內(nèi)處理異常,需要確定此處是否適合將某些數(shù)據(jù)寫入響應(yīng)中。如果 Action 結(jié)果在執(zhí)行中途拋出異常,而 header 也已被更新到客戶端,那么將沒有任何可靠的機(jī)制來發(fā)送失敗代碼。
對于 IAsyncResultFilter 的 OnResultExecutionAsync 方法來講,它具有 OnResultExecuting 和 OnResultExecuted 的功能。在 ResultExecutionDelegate 上調(diào)用 await next() 將執(zhí)行后續(xù)的結(jié)果過濾器和 Action 結(jié)果,并返回 ResultExecutedContext。如果將 ResultExecutingContext.Cancel 值為 true 并不調(diào)用 ResultExectionDelegate,則將在內(nèi)部短路 OnResultExecutionAsync。
你可以覆蓋內(nèi)建的 ResultFilterAttribute 特性,創(chuàng)建定制的結(jié)果過濾器, AddHeaderAttribute 類便是一例結(jié)果過濾器。
提示
若你需要為響應(yīng)增加 header,在 Action 結(jié)果執(zhí)行前如是做。否則響應(yīng)就會(huì)被發(fā)送到客戶端,屆時(shí)改之晚矣。故對于結(jié)果過濾器而言,為響應(yīng)增加 header 需要在 OnResultExecuting 中處理(而不是在 OnResultExecuted 中)。
過濾器對比中間件
一般情況下,過濾器用于處理業(yè)務(wù)與應(yīng)用程序的橫切關(guān)注點(diǎn)。它的用法很像 中間件 。從能力上來講過濾器酷似中間件,但過濾器的作用范圍很大,因此允許你將它插入到應(yīng)用程序中需要使用到它的場合中,比如在視圖之前或在模型綁定之后。過濾器是 MVC 的一部分,可以訪問 MVC 的上下文以及構(gòu)造函數(shù)。比方說,中間件不能簡單地直接察覺請求中模型驗(yàn)證是否生成了錯(cuò)誤并對此作出響應(yīng),而過濾器卻能做到。
如果想要嘗試一下過濾器,可以下載、測試并修改樣例 。
轉(zhuǎn)載于:https://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-4_4_3-filters.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用vue+webpack搭建的前端项目结
- 下一篇: leetcode 8. String t