asp.net core系列 68 Filter管道過濾器
一.概述
本篇詳細了解一下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的階段順序如下所示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM9AnYldnJwAzN9c3Pn5GcuQ0MlMWbidXNT5keBR0TykEVOBTRq1EdVRVT4VFRNlXSq1UeRRVTzEEVPhXQq1EdBpmTxUEVOhHO550drRVT3lkeMdXWU5EeVRVT2NmMiNnSywEd5ITW110MaZHetlVdO1GT0UERNl3YXJGc5kHT20ESjBjUIF2Lc12bj5SYphXa5VWen5WY35iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.png)
上面每種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) 結果被視為失敗。
更詳細的資料參考官網文檔,後續在實際項目中應用到的篩選器,将會再本篇繼續補上。
參考資料:
官方文檔
官方示例代碼
posted on 2019-07-16 09:37 花陰偷移 閱讀(...) 評論(...) 編輯 收藏