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