ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器
原文:Filters
作者:Steve Smith
翻譯:劉怡(AlexLEWIS)
校對:何鎮汐
ASP.NET MVC 過濾器 可在執行管道的前后特定階段執行代碼。過濾器可以配置為全局有效、僅對控制器有效或是僅對 Action 有效。
查看或下載演示代碼.
過濾器如何工作?
不同的過濾器類型會在執行管道的不同階段運行,因此它們各自有一套適用場景。根據你實際要解決的問題以及在請求管道中執行的位置來選擇創建不同的過濾器。運行于 MVC Action 調用管道內的過濾器有時被稱為 過濾管道 ,當 MVC 選擇要執行哪個 Action 后就會先執行該 Action 上的過濾器。
不同過濾器在管道的不同位置運行。像授權這樣的過濾器只運行在管道的靠前位置,并且其后也不會跟隨 action。其它過濾器(如 action 過濾器等)可以在管道的其它部分之前或之后執行,如下所示。
選擇過濾器
授權過濾器 用于確定當前用戶的請求是否合法。
資源過濾器 是授權之后第一個用來處理請求的過濾器,也是最后一個接觸到請求的過濾器(因為之后就會離開過濾器管道)。在性能方面,資源過濾器在實現緩存或短路過濾器管道尤其有用。
Action 過濾器 包裝了對單個 action 方法的調用,可以將參數傳遞給 action 并從中獲得 action result。
異常過濾器 為 MVC 應用程序未處理異常應用全局策略。
結果過濾器 包裝了單個 action result 的執行,當且僅當 action 方法成功執行完畢后方才運行。它們是理想的圍繞視圖執行或格式處理的邏輯(所在之處)。
實現
所有過濾器均可通過不同的接口定義來支持同步和異步實現。根據你所需執行的任務的不同來選擇同步還是異步實現。從框架的角度來講它們是可以互換的。
同步過濾器定義了 OnStageExecuting 方法和 OnStageExecuted 方法(當然也有例外)。OnStageExecuting 方法在具體事件管道階段之前調用,而 OnStageExecuted 方法則在之后調用(比如當 Stage 是 Action 時,這兩個方法便是 OnActionExecuting 和 OnActionExecuted,譯者注)。
異步過濾器定義了一個 On?Stage?ExecutionAsync 方法,可以在具體管道階段的前后運行。On?Stage?ExecutionAsync 方法被提供了一個 Stage?ExecutionDelegate 委托,當調用時該委托會執行具體管道階段的工作,然后等待其完成。
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}} }注解
只能 實現一個過濾器接口,要么是同步版本的,要么是異步版本的,魚和熊掌不可兼得 。如果你需要在接口中執行異步工作,那么就去實現異步接口。否則應該實現同步版本的接口。框架會首先檢查是不是實現了異步接口,如果實現了異步接口,那么將調用它。不然則調用同步接口的方法。如果一個類中實現了兩個接口,那么只有異步方法會被調用。最后,不管 action 是同步的還是異步的,過濾器的同步或是異步是獨立于 action 的。
過濾器作用域
過濾器具有三種不同級別的 作用域 。你可以在特定的 action 上用特性(Attribute)的方式使用特定的過濾器;也可以在控制器上用特性的方式使用過濾器,這樣就會將效果應用在控制器內所有的 action 上;或者注冊一個全局過濾器,它將作用于整個 MVC 應用程序下的每一個 action。
如果想要使用全局過濾器的話,在你配置 MVC 的時候在 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>(); }過濾器可通過類型添加,也可以通過實例添加。如果通過實例添加,則該實例會被用于每一個請求。如果通過類型添加,則將會 type-activated(意思是說每次請求都會創建一個實例,其所有構造函數依賴項都將通過 DI 來填充)。通過類型添加過濾器相當于 filters.Add(new TypeFilterAttribute(typeof(MyFilter))) 。
把過濾器接口的實現當做 特性(Attributes) 使用是極為方便的。過濾器特性(filter attributes)可應用于控制器(Controllers)和 Action 方法。框架包含了內置的基于特性的過濾器,你可繼承它們或另外定制。比方說,下例過濾器繼承了 ResultFilterAttribute ,并重寫(override)了 OnResultExecuting 方法(在響應中增加了一個頭信息)。
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);}} }特性允許過濾器接收參數,如下例所示。可將此特性加諸控制器(Controller)或 Action 方法,并為其指定所需 HTTP 頭的名稱和值,并將該 HTTP 頭加入響應中:
[AddHeader("Author", "Steve Smith @ardalis")] public class SampleController : Controller {public IActionResult Index(){return Content("Examine the headers using developer tools.");} }Index Action 的結果如下所示:響應的頭信息顯示在右下角。
以下幾種過濾器接口可以自定義為相應特性的實現。
過濾器特性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
取消與短路
通過設置傳入過濾器方法的上下文參數中的 Result 屬性,可以在過濾器管道的任意一點短路管道。比方說,下面的 ShortCircuitingResourceFilter 將阻止所有它之后管道內的所有過濾器,包括所有 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 方法。然而,由于首先運行的是 ShortCircuitingResourceFilter ,它短路了剩下的管道,SomeResource 上的 AddHeader 過濾器不會運行。如果這兩個過濾器都以 Action 方法級別出現,它們的結果也會是一樣的(只要 ShortCircuitingResourceFilter 首先運行,請查看 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 中配置。基于特性的過濾器如果不需要任何依賴項的話,可以簡單地繼承一個與已存在的過濾器相對應的特性類型。如果要創建一個 非 全局作用域、但需要從依賴注入(DI)中獲得依賴項的過濾器,在它們上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,這樣就可用于控制器或 action 了。
依賴注入
以特性形式實現的、直接添加到控制器(Controller)類或 Action 方法的過濾器,其構造函數不得由 依賴注入 (DI)提供依賴項。其原因在于特性所需的構造函數參數必須由使用處直接提供。這是特性原型機理的限制。
不過,如果過濾器需要從 DI 中獲得依賴項,那么有幾種辦法可以實現,可在類(class)或 Action 方法使用:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory 實現你的特性
TypeFilter 將為其依賴項從 DI 中使用服務(services)來實例化一個實例。ServiceFilter 則從 DI 中取回一個過濾器實例。下例中將演示如何使用 ServiceFilter:
[ServiceFilter(typeof(AddHeaderFilterWithDi))] public IActionResult Index() {return View(); }如果在 ConfigureServices 中直接使用未經注冊的 ServiceFilter 過濾器,則會拋出以下異常:
System.InvalidOperationException: No service for type 'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.為避免此異常,你必須在 ConfigureServices 中為 AddHeaderFilterWithDI 類型注冊:
services.AddScoped<AddHeaderFilterWithDi>();ServiceFilterAttribute 實現了 IFilterFactory 接口,后者暴露了創建 IFilter 實例的單一方法。在 ServiceFilterAttribute 中,接口 IFilterFactory 中定義的 CreateInstance 方法被實現為用于從服務容器(DI)加載指定類型。
TypeFilterAttribute 很像 ServiceFilterAttribute (它同樣是 IFilterFactory 的實現),但此類型并非直接解析自 DI 容器。
相反,它通過使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來實例化類型。
由于這種不同,使用 TypeFilterAttribute 引用的類型不需要在使用之前向容器注冊(但它們依舊將由容器來填充其依賴項)。同樣地,TypeFilterAttribute 能可選地接受該類型的構造函數參數。下例演示如何向使用 TypeFilterAttribute 修飾的類型傳遞參數:
[TypeFilter(typeof(AddHeaderAttribute),Arguments = new object[] { "Author", "Steve Smith (@ardalis)" })] public IActionResult Hi(string name) {return Content($"Hi {name}"); }若是你有一個簡單的不需要任何參數的、但其構造函數需要通過 DI 填充依賴項的過濾器的話,你可以通過繼承 TypeFilterAttribute,在類(class)或方法(method)上使用自己命名的特性(來取代 [TypeFilter(typeof(FilterType))])。下例過濾器向你展示這是如何實現的:
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] 這樣的語法應用于類或方法,而不必使用 [TypeFilter] 或 [ServiceFilter]。
注解
應避免純粹為記錄日志而創建和使用過濾器,這是因為 內建的框架日志功能 應該已經提供了你所需的功能。如果你要把日志記錄功能放入過濾器中,它應專注于業務領域或過濾器的具體行為,而不是 MVC Action 或框架事件。
IFilterFactory 實現了 IFilter。因此,在過濾器管道的任何地方 IFilterFactory 實例都可當做 IFilter 實例來使用。當框架準備調用過濾器,將試圖把其強制轉換為 IFilterFactory。如果轉換成功,將通過調用 CreateInstance 方法創建即將被調用的 IFilter 實例。因為過濾器管道不需要在應用程序啟動時顯式設置了,所以這是一種非常靈活的設計。
你可以在自己的特性實現中實現 IFilterFactory 接口,以此來實現另一種創建過濾器的方法:
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){}}排序
過濾器可應用于 Action 方法、控制器(Controller,通過特性(attribute)的形式)或添加到全局過濾器集合中。其作用域通常還決定了其執行順序。最靠近 Action 的過濾器首先執行;通常來講通過重寫行為而不是顯式設置順序來改變順序。這有時被稱為“俄羅斯套娃”,因為每一個作用范圍都包裹了前一個作用范圍,就像是 套娃 那般。
除了作用范圍,過濾器還可以通過實現 IOrderedFilter 來重寫它們的執行順序。該接口只是簡單地暴露了 int Order 屬性,然后執行時根據該數字正排序(數字越小,越先執行)后依次執行過濾器。所有內建過濾器(包括 TypeFilterAttribute 和 ServiceFilterAttribute)都實現了 IOrderedFilter 接口,因此當你將過濾器以特性的方式用于類(class)或方法(method)時你可以指定每一個過濾器的執行順序。默認情況下所有內建過濾器的 Order 屬性值都為 0,因此作用范圍就當做決定性的因素(除非存在不為 0 的 Order 值)。
每個繼承自 Controller 基類的控制器(Controller)都包含 OnActionExecuting 和 OnActionExecuted 方法。這些方法為給定的 Action 包裝了過濾器,它們分別在最先和最后運行。基于作用范圍的順序(假設沒有為過濾器的 Order 設置任何值):
注解
當過濾器將啟動運行、需要決定過濾器執行順序時,IOrderedFilter 會向外宣布自己的作用范圍。過濾器首先通過 order 來排序,然后通過作用范圍來決定。如果不設置,則 Order 默認為 0。
為在基于作用范圍的排序中修改默認值,你須在類一級(class-level)或方法一級(method-level)的過濾器上顯式設置 Order 屬性。比如為方法一級的特性增加 Order=-1:
[MyFilter(Name = "Method Level Attribute", Order=-1)]這種情況下,小于 0 的值將確保該過濾器在全局過濾器和類一級過濾器之前運行(假設它們的 Order 屬性均未設置)。
新的排序可能是這樣的:
注解
Controller 類的方法總是在所有過濾器之前和之后運行。這些方法并未實現為 IFilter 實現,同時它們不參與 IFilter 的排序算法。
授權過濾器
授權過濾器 控制對 action 方法的訪問,也是過濾器管道中第一個被執行的過濾器。它們只有一個前置階段,不像其它大多數過濾器支持前置階段方法和后置階段方法。只有當你使用自己的授權框架時才需要定制授權過濾器。謹記勿在授權過濾器內拋出異常,這是因為所拋出的異常不會被處理(異常過濾器也不會處理它們)。此時記錄該問題或尋求其它辦法。
更多請訪問 Authorization
資源過濾器
資源過濾器 要么實現 IResourceFilter 接口,要么實現 IAsyncResourceFilter 接口,它們執行于大多數過濾器管道(只有 授權過濾器 在其之前運行,其余所有過濾器以及 Action 處理均出現在其 OnResourceExecuting 和 OnResourceExecuted 方法之間)。當你需要短路絕大多數正在進行的請求時,資源過濾器特別有用。資源過濾器的一個典型例子是緩存,如果響應已經被緩存,過濾器會立即將之置為結果以避免后續 Action 的多余操作過程。
上面所說的是一個 短路資源過濾器 的例子。下例是一個非常簡單的緩存實現(請勿將之用于生產環境),只能與 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 中,如果結果已經在靜態字段緩存中,Result 屬性將被設置到 context 上,同時 Action 被短路并返回緩存的結果。在 OnResourceExecuted 方法中,如果當前其請求的鍵未被使用過,那么 Result 就會被保存到緩存中,用于之后的請求。
如下所示,把這個過濾器用于類或方法之上:
[TypeFilter(typeof(NaiveCacheResourceFilterAttribute))] public class CachedController : Controller {public IActionResult Index(){return Content("This content was generated at " + DateTime.Now);} }Action 過濾器
Action 過濾器 要么實現 IActionFilter 接口,要么實現 IAsyncActionFilter 接口,它們可以在 action 方法執行的前后被執行。Action 過濾器非常適合放置諸如查看模型綁定結果、或是修改控制器或輸入到 action 方法的邏輯。另外,action 過濾器可以查看并直接修改 action 方法的結果。
OnActionExecuting 方法在 action 方法執行之前運行,因此它可以通過改變 ActionExecutingContext.ActionArguments 來控制 action 的輸入,或是通過 ActionExecutingContext.Controller 控制控制器(Controller)。OnActionExecuting 方法可以通過設置 ActionExecutingContext.Result 來短路 action 方法的操作及其后續的過濾器。OnActionExecuting 方法通過拋出異常也可以阻止 action 方法和后續過濾器的處理,但會當做失敗(而不是成功)的結果來處理。
OnActionExecuted 方法在 action 方法執行之后才執行,并且可以通過 ActionExecutedContext.Result 屬性查看或控制 action 的結果。如果 action 在執行時被其它過濾器短路,則 ActionExecutedContext.Canceled 將會被置為 true。如果 action 或后續的 action 過濾器拋出異常,則 ActionExecutedContext.Exception 會被設置為一個非空值。有效「處理」完異常后把 ActionExecutedContext.Exception 設置為 null,那么 ActionExectedContext.Result 會像從 action 方法正常返回值那樣被處理。
對于 IAsyncActionFilter 接口來說,它的 OnActionExecutionAsync 方法結合了 OnActionExecuting 和 OnActionExecuted 的所有能力。調用 await next() 后,ActionExecutionDelegate 將會執行所有的后續 action 過濾器以及 action 方法,并返回 ActionExecutedContext 。
如果想要在 OnActionExecutionAsync 內部短路,那么就為 ActionExecutingContext.Result 分配一個結果實例,并且不要調用 ActionExecutionDelegate 即可。
異常過濾器
異常過濾器 實現了 IExceptionFilter 接口或 IAsyncExceptionFilter 接口。
異常過濾器用于處理「未處理異常」,包括發生在 Controller 創建及 模型綁定 期間出現的異常。它們只在管道內發生異常時才會被調用。它們提供了一個單一的位置實現應用程序內的公共異常處理策略。框架提供了抽象的 ExceptionFilterAttribute ,你根據自己的需要繼承這個類。異常過濾器適用于捕獲 MVC Action 內出現的異常,但它們不及錯誤處理中間件(error handling middleware)靈活。一般來講優先使用中間件,只有在需要做一些基于所選 MVC Action 的、有別于錯誤處理的工作時才選擇使用過濾器。
提示
對于應用程序中不同 action 需要使用不同的錯誤處理方式,并向 Views/HTML 暴露 API 端點或 action 的錯誤處理結果。API 端點用 JSON 返回錯誤信息,而基于視圖的 action 則返回錯誤頁面(HTML 頁面)。
異常過濾器不應有兩個事件(對于前置或后置而言),它們只實現 OnException (或 OnExceptionAsync )。以參數形式傳入 OnException 的 ExceptionContext 包含了所發生的 Exception。如果把 context.Exception 設置為 null,其效果相當于你已處理該異常,所以該次請求會像沒發生過異常那樣繼續處理(一般會返回 HTTP 200 OK 狀態)。下例過濾器中使用定制的開發者錯誤視圖來顯示開發環境中應用程序所出現異常的詳細信息:
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;}} }結果過濾器
實現了 IResultFilter 或 IAsyncResultFilter 接口的 結果過濾器 在 Action Result 執行體的周圍執行。當 Action 或 Action 過濾器產生 Action 結果時,只有成功運行的才會執行結果過濾器。如果異常過濾器處理了異常,那么結果過濾器就不會運行——除非異常過濾器將異常設置為null(Exception = null)。
注解
正在執行的結果種類取決于相關 Action。MVC Action 所返回的 View 將包含 Razor(將其作為正在處理的 ViewResult 的一部分)。API 方法則將執行一些序列化工作作為其執行結果的一部分。了解更多請移步 action 結果。
結果過濾器適用于任何需要直接環繞 View 或格式化處理的邏輯。結果過濾器可以替換或更改 Action 結果(而后者負責產生響應)。
OnResultExecuting 方法運行于 Action 結果執行之前,故其可通過 ResultExecutingContext.Result 操作 Action 結果。如果將 ResultExecutingContext.Cancel 設置為 true,則 OnResultExecuting 方法可短路 Action 結果以及后續結果過濾器的執行。如果發生了短路,MVC 將不會修改響應,所以當發生短路時,為避免生成空響應,你一般應該直接去修改響應對象。如果在 OnResultExecuting 方法內拋出異常,那么也將阻止 Action 結果以及后續過濾器的執行,但會被當做失敗結果(而非成功結果)。
OnResultExecuted 方法運行于 Action 結果執行之后。也就是說,如果沒有拋出異常,響應可能就會被發送到客戶端且不可再修改。如果 Action 結果在執行中被其它過濾器短路,則 ResultExecutedContext.Canceled 將被置為 true。如果 Action 結果或后續結果過濾器拋出異常,則 ResultExecutedContext.Exception 將被置為非空值(non-null value)。把 ResultExecutedContext.Exception 設置為 null 后會影響到異常的“處理”,這將阻止異常在之后的管道內被 MVC 重新拋出。如果在結果過濾器內處理異常,需要確定此處是否適合將某些數據寫入響應中。如果 Action 結果在執行中途拋出異常,而 header 也已被更新到客戶端,那么將沒有任何可靠的機制來發送失敗代碼。
對于 IAsyncResultFilter 的 OnResultExecutionAsync 方法來講,它具有 OnResultExecuting 和 OnResultExecuted 的功能。在 ResultExecutionDelegate 上調用 await next() 將執行后續的結果過濾器和 Action 結果,并返回 ResultExecutedContext。如果將 ResultExecutingContext.Cancel 值為 true 并不調用 ResultExectionDelegate,則將在內部短路 OnResultExecutionAsync。
你可以覆蓋內建的 ResultFilterAttribute 特性,創建定制的結果過濾器, AddHeaderAttribute 類便是一例結果過濾器。
提示
若你需要為響應增加 header,在 Action 結果執行前如是做。否則響應就會被發送到客戶端,屆時改之晚矣。故對于結果過濾器而言,為響應增加 header 需要在 OnResultExecuting 中處理(而不是在 OnResultExecuted 中)。
過濾器對比中間件
一般情況下,過濾器用于處理業務與應用程序的橫切關注點。它的用法很像 中間件 。從能力上來講過濾器酷似中間件,但過濾器的作用范圍很大,因此允許你將它插入到應用程序中需要使用到它的場合中,比如在視圖之前或在模型綁定之后。過濾器是 MVC 的一部分,可以訪問 MVC 的上下文以及構造函數。比方說,中間件不能簡單地直接察覺請求中模型驗證是否生成了錯誤并對此作出響應,而過濾器卻能做到。
如果想要嘗試一下過濾器,可以下載、測試并修改樣例 。
轉載于:https://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-4_4_3-filters.html
總結
以上是生活随笔為你收集整理的ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用vue+webpack搭建的前端项目结
- 下一篇: ASP.NET Core 中文文档 第四