本篇將介紹 ASP.NET Core 的五種 Filter 運作方式。
包含 Authorization Filter、Resource Filter、Action Filter、Exception Filter 及 Result Filter。
1. Filter 介紹
Filter 的作用是在 Action 執行前或執行後做一些加工處理。
某種程度來看,會跟 Middleware 很像,但執行的順序略有不同,用對 Filter 不僅可以減少程式碼,還可以減省執行效率。
ASP.NET Core 有以下五種 Filter 可以使用:
- Authorization Filter
Authorization 是五種 Filter 中優先序最高的,通常用於驗證 Request 合不合法,不合法後面就直接跳過。 - Resource Filter
Resource 是第二優先,會在 Authorization 之後,Model Binding 之前執行。通常會是需要對 Model 加工處裡才用。 - Action Filter
最容易使用的 Filter,封包進出都會經過它,使用上沒什麼需要特別注意的。跟 Resource Filter 很類似,但並不會經過 Model Binding。 - Exception Filter
異常處理的 Exception。 - Result Filter
當 Action 完成後,最終會經過的 Filter。
2. Filter 運作方式
ASP.NET Core 的每個 Request 都會先經過已註冊的 Middleware 接著才會執行 Filter,除了會依照上述的順序外,同類型的 Filter 都會以先進後出的方式處裡封包。
Response 在某些 Filter 並不會做處理,會值接 Bypass。Request 及 Response 的運作流程如下圖:
黃色箭頭是正常情況流程
灰色箭頭是異常處理流程
3. 建立 Filter
ASP.NET Core 的 Filter 基本上跟 ASP.NET MVC 的差不多。
上述的五種 Filter 範例分別如下:
3.1. Authorization Filter
AuthorizationFilter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13
| using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters;
namespace MyWebsite.Filters { public class AuthorizationFilter : IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); } } }
|
非同步的方式:
1 2 3 4 5 6 7 8
| public class AuthorizationFilter : IAsyncAuthorizationFilter { public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { await context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); } }
|
3.2. Resource Filter
ResourceFilter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters;
namespace MyWebsite.Filters { public class ResourceFilter : IResourceFilter { public void OnResourceExecuting(ResourceExecutingContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); }
public void OnResourceExecuted(ResourceExecutedContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } } }
|
非同步的方式:
1 2 3 4 5 6 7 8 9 10 11 12
| public class ResourceFilter : IAsyncResourceFilter { public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { await context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n");
await next();
await context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } }
|
3.3. Action Filter
ActionFilter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters;
namespace MyWebsite.Filters { public class ActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); }
public void OnActionExecuted(ActionExecutedContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } } }
|
非同步的方式:
1 2 3 4 5 6 7 8 9 10 11 12
| public class ActionFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { await context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n");
await next();
await context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } }
|
3.4. Result Filter
ResultFilter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters;
namespace MyWebsite.Filters { public class ResultFilter : IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); }
public void OnResultExecuted(ResultExecutedContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } } }
|
非同步的方式:
1 2 3 4 5 6 7 8 9 10 11 12
| public class ResultFilter : IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { await context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n");
await next();
await context.HttpContext.Response.WriteAsync($"{GetType().Name} out. \r\n"); } }
|
3.5. Exception Filter
ExceptionFilter.cs
1 2 3 4 5 6 7 8 9 10 11 12 13
| using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters;
namespace MyWebsite.Filters { public class ExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); } } }
|
非同步的方式:
1 2 3 4 5 6 7 8 9
| public class ExceptionFilter : IAsyncExceptionFilter { public Task OnExceptionAsync(ExceptionContext context) { context.HttpContext.Response.WriteAsync($"{GetType().Name} in. \r\n"); return Task.CompletedTask; } }
|
4. 註冊 Filter
Filter 有兩種註冊方式,第一種是全域註冊,另一種是用 Attribute 區域註冊的方式,只套用在特定的 Action。
4.1. 全域註冊
在 Startup.cs 註冊 Filter,這樣就可以套用到所有的 Request。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(config => { config.Filters.Add(new ResultFilter()); config.Filters.Add(new ExceptionFilter()); config.Filters.Add(new ResourceFilter()); }); } }
|
4.2. 區域註冊
ASP.NET Core 在 Attribute 註冊 Filter 的方式跟 ASP.NET MVC 有一點不一樣,要透過 [TypeFilter(type)]
。
在 Controller 或 Action 上面加上 [TypeFilter(type)]
就可以區域註冊 Filter。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| namespace MyWebsite.Controllers { [TypeFilter(typeof(AuthorizationFilter))] public class HomeController : Controller { [TypeFilter(typeof(ActionFilter))] public void Index() { Response.WriteAsync("Hello World! \r\n"); } [TypeFilter(typeof(ActionFilter))] public void Error() { throw new System.Exception("Error"); } } }
|
執行結果
程式碼下載
asp-net-core-filters
參考
ASP.NET Core Filters