一.概述 本篇詳細瞭解一下asp.net core filters,filter叫"篩選器"也叫"過濾器",是請求處理管道中的特定階段之前或之後運行代碼。filter用於處理橫切關註點。 橫切關註點的示例包括:錯誤處理、緩存、配置、授權和日誌記錄。 filter可以避免重覆代碼,通過Attribut ...
一.概述
本篇詳細瞭解一下asp.net core filters,filter叫"篩選器"也叫"過濾器",是請求處理管道中的特定階段之前或之後運行代碼。filter用於處理橫切關註點。 橫切關註點的示例包括:錯誤處理、緩存、配置、授權和日誌記錄。 filter可以避免重覆代碼,通過Attribute特性來實現filter過濾。Filter適應於 Razor Pages, API controllers, mvc controllers。filter基類是IFilterMetadata 介面,該介面只是用來標記是一個filter過濾器。
前段時間在項目中實現了IAsyncAuthorizationFilter介面對用戶訪問controller或action進行了授權,在OnAuthorizationAsync方法中使用context.Result可使管道短道。IAsyncAuthorizationFilter是屬於授權過濾器中的一種。勿在授權過濾器內拋出異常,這是因為所拋出的異常不會被處理。下麵是簡要的實現授權代碼:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class PermissionFilter : Attribute,IAsyncAuthorizationFilter { public Task OnAuthorizationAsync(AuthorizationFilterContext context) { IIdentity user = context.HttpContext.User.Identity; if (!user.IsAuthenticated) { //跳轉到登錄頁 context.Result = new LocalRedirectResult(url); return Task.CompletedTask; } //根據當前用戶,判斷當前訪問的action,沒有許可權時返回403錯誤 context.Result = new ForbidResult(); return Task.CompletedTask; } }
在官方文檔中說到:"自定義授權篩選器Filter需要自定義授權框架, 建議配置授權策略或編寫自定義授權策略,而不是編寫自定義Filter篩選器"。
這裡的意思是說,如果在項目中不使用asp.net core自帶的identity,那麼需要自定義授權框架,也就是需要自己建立一套用戶許可權表。 如果使用了identity建議配置授權策略或編寫自定義授權策略。如果不用identity自己建立用戶許可權表,那麼就可以編寫自定義Filter篩選器。我在項目開發中是自己建立了用戶許可權表沒有用identity。因為使用identity表還需要擴展欄位,而且與EF core結合緊密,如果不使用EF core需要重寫訪問identity表的實現。
下麵是identity與ef core相關的代碼:
services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); -- TContext必須是DbContext類型 public static IdentityBuilder AddEntityFrameworkStores<TContext>(this IdentityBuilder builder) where TContext : DbContext
二.Filter 篩選器類型
Authorization filters 授權篩選器
Resource filters 資源篩選器
Action filters 操作篩選器 (Razor Pages 中使用 IPageFilter 和 IAsyncPageFilter)
Exception filters 異常篩選器
Result filters 結果篩選器
每種Filter 類型都在Filter 管道中的不同階段執行,當用戶請求進來時,經過Filter管道,執行Filter的階段順序如下所示:
上面每種Filter 類型都有自己的介面,都同時支持同步和非同步實現,上面的授權示例就是一個非同步實現授權篩選器IAsyncAuthorizationFilter。Filter 介面或直接或間接實現了IFilterMetadata介面,IFilterMetadata介面只是用來標記是一個Filter 。
三.filter篩選器作用域
(1) 將 attribute特性應用在 action上。
(2) 將 attribute特性應用在 controller上。
(3) 全局篩選器應用在controller和action上
下麵使用全局篩選器,使用MvcOptions.Filters 集合,添加三個篩選器,如下麵的代碼所示:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader", "Result filter added to MvcOptions.Filters")); // An instance options.Filters.Add(typeof(MySampleActionFilter)); // By type options.Filters.Add(new SampleGlobalActionFilter()); // An instance }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
//實現了內置篩選器屬性 public class AddHeaderAttribute : ResultFilterAttribute //實現了操作篩選器 public class MySampleActionFilter : IActionFilter //實現了操作篩選器 public class SampleGlobalActionFilter : IActionFilter
四.內置篩選器Attribute屬性
下麵都是filter內置的屬性類,需要去實現這些抽象屬性類。
ActionFilterAttribute
ExceptionFilterAttribute
ResultFilterAttribute
FormatFilterAttribute
ServiceFilterAttribute
TypeFilterAttribute
下麵一個示例是為響應添加標頭,開發人員來實現ResultFilterAttribute抽象類:
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); } }
通過使用屬性,篩選器可接收參數,將 AddHeaderAttribute
添加到控制器或操作方法,並指定 HTTP 標頭的名稱和值,如下所示:
[AddHeader("Author", "Steve Smith @ardalis")] public IActionResult Hello(string name) { return Content($"Hello {name}"); }
五.Filter三種依賴關係註入
(1)ServiceFilterAttribute
(2)TypeFilterAttribute
(3)在屬性上實現 IFilterFactory。
5.1 ServiceFilterAttribute演示
下麵是在 ConfigureServices 中註冊服務篩選器實現類型,內置的ServiceFilterAttribute會從DI 檢索篩選器實例。
public class AddHeaderResultServiceFilter : IResultFilter { private ILogger _logger; public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>(); } public void OnResultExecuting(ResultExecutingContext context) { var headerName = "OnResultExecuting"; context.HttpContext.Response.Headers.Add( headerName, new string[] { "ResultExecutingSuccessfully" }); _logger.LogInformation($"Header added: {headerName}"); } public void OnResultExecuted(ResultExecutedContext context) { // Can't add to headers here because response has started. } }
在以下代碼中,AddHeaderResultServiceFilter 將添加到 DI 容器中:
services.AddScoped<AddHeaderResultServiceFilter>();
在以下代碼中,通過ServiceFilter 屬性,將從 DI 中檢索 AddHeaderResultServiceFilter 篩選器的實例:
[ServiceFilter(typeof(AddHeaderResultServiceFilter))] public IActionResult Index() { return View(); }
六. 各種篩選器介紹
6.1 Authorization filters
是篩選器管道中第一個運行的篩選器。是控制對Action方法的訪問。
在Action之前執行的方法,沒有在Action之後執行的方法。
註意:不要在授權篩選器中引發異常
場景:
如果使用identity,可用配置授權策略或編寫自定義授權策略。詳情查看identity部分。
如果不使用identity,可編寫自定義篩選器。
6.2 Resource filters
實現 IResourceFilter 或 IAsyncResourceFilter 介面。
它執行會覆蓋篩選器管道的絕大部分。
它在授權篩選器之後運行。
場景:
可以防止模型綁定訪問表單數據。
用於上傳大型文件,以防止表單數據被讀入記憶體。
6.3 Action filters
實現 IActionFilter 或 IAsyncActionFilter 介面。
它圍繞著Action方法的執行。
它方法中有個重要屬性ActionArguments ,用於讀取action的輸入參數。
場景:
可以在該介面的OnActionExecuting方法中進行驗證模型狀態(ModelState.IsValid),如果狀態無效,則返回錯誤(這個場景很有用)。
6.4 Exception filters
實現 IExceptionFilter 或 IAsyncExceptionFilter。
它沒有之前和之後的事件,
可實現該介面的 OnException 或 OnExceptionAsync方法。
處理 Razor 頁面或控制器創建、模型綁定、操作篩選器或操作方法中發生的未經處理的異常。
若要處理異常(不再throw),請將 ExceptionHandled 屬性設置為 true。
場景:
實現常見的錯誤處理策略。
非常適合捕獲發生在action中的異常。
6.5 Result filters
實現 IResultFilter 或 IAsyncResultFilter 或 IAlwaysRunResultFilter 或 IAsyncAlwaysRunResultFilter
它執行圍繞著action結果的執行。當異常篩選器處理異常時,不執行結果篩選器。
如果在 IResultFilter.OnResultExecuting 中引發異常,則會導致:
(1) 阻止action結果和後續篩選器的執行。
(2) 結果被視為失敗。
更詳細的資料參考官網文檔,後續在實際項目中應用到的篩選器,將會再本篇繼續補上。
參考資料: