ASP.NET MVC 中的過濾器容許在執行管道中的特定階段以前或以後運行代碼。能夠對全局,也能夠對每一個控制器或每一個操做配置過濾器。算法
1.過濾器如何工做緩存
不一樣的過濾器類型在管道中的不一樣階段執行,所以具備各自的與其場景。根據須要執行的任務以及須要執行的請求管道中的位置,選擇要建立的過濾器類型。過濾器在 MVC 操做調用管道中運行,有時也稱爲過濾管道,在 MVC 中選擇要執行的操做後,執行操做上的過濾器,如圖:框架
不一樣的過濾器在管道內的不一樣位置執行。像受權過濾器這樣的過濾器只在管道中靠前的位置執行。其餘過濾器,如操做(Action)過濾器,能夠在管道執行的其餘部分以前和以後執行,如圖:異步
1.選擇過濾器async
受權過濾器用於肯定當前請求用戶是否被受權。ide
資源過濾器是在受權以後第一個處理請求的過濾器,也是最後一個在請求離開過濾管道時接觸請求的過濾器。在性能方面,對實現緩存或者對過濾管道進行短路 特別有用。函數
操做過濾器包裝對單個操做方法的調用,而且能夠處理傳遞到操做的參數以及從操做返回的操做結果。性能
異常過濾器用於對 MVC 應用程序中未處理的異常應用全局策略。ui
結果過濾器包裝單個操做結果的執行,而且盡在操做執行成功時運行。它們必須是圍繞視圖執行或格式化程序執行的邏輯的理想選擇。spa
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; } //實現IFilterFactory public 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 的一部分,能夠訪問其上下文和構造函數。例如,中間件很難檢測到請求的模型驗證是否產生錯誤,而且作出相應的響應。