一、篩選器
通過使用篩選器可在請求處理管道中的特定階段之前或之后運(yùn)行代碼。
這即是我們經(jīng)常聽到的面向切面編程AOP(Aspect Oriented Programming)技術(shù),AOP通過預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
篩選器在 ASP.NET Core 操作調(diào)用管道(有時(shí)稱為篩選器管道)內(nèi)運(yùn)行。 篩選器管道在 ASP.NET Core 選擇了要執(zhí)行的操作之后運(yùn)行:
Asp.Net Core 關(guān)注的切面點(diǎn)
包括錯(cuò)誤處理、緩存、配置、授權(quán)和日志記錄篩選器,這個(gè)是說通過篩選器可以實(shí)現(xiàn)對以上關(guān)注點(diǎn)的一些操作。
在Asp.Net Core中有如下幾種類型的篩選器:
其中部分是內(nèi)置篩選器,比如授權(quán),響應(yīng)緩存已經(jīng)幫我們內(nèi)置進(jìn)了框架,我們只需要配置即可使用;其他篩選器是可以自定義處理邏輯的。
下圖展示了篩選器類型在篩選器管道中的交互方式和執(zhí)行順序:
二、操作型篩選器
第一部分主要是對篩選器的一個(gè)梳理,有些重點(diǎn)的提煉,詳情查看文檔,因?yàn)槲臋n部分理解起來比較晦澀,比如關(guān)注點(diǎn)是關(guān)注點(diǎn),知識說篩選器可以對這些關(guān)注點(diǎn)啟到作用,篩選器是固定的幾種,不要被文檔中的這種描述搞暈了,一會兒有這幾種,怎么到下面又是另外幾種,要注意區(qū)分重點(diǎn)。
操作篩選器可以實(shí)現(xiàn)接口IActionFilter
,在接口中有兩個(gè)方法,OnActionExecuting 在調(diào)用操作方法之前執(zhí)行。 OnActionExecuted 在操作方法返回之后執(zhí)行。
- 先建WebAPI項(xiàng)目 WebAPI_Filter
- 建一個(gè) FilterController,并創(chuàng)建Get請求Test
namespace WebAPI_Filter.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class FilterController : ControllerBase
{
[HttpGet]
public string Test()
{
return "測試Filter!";
}
}
}
- 創(chuàng)建ActionFilter 篩選器
namespace WebAPI_Filter.Filter
{
public class MyActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl()+ " 執(zhí)行之后!");
}
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 執(zhí)行之前!");
}
}
}
- 在Program.cs里面添加篩選器
執(zhí)行測試接口
三、篩選器作用域和執(zhí)行順序
上面直接在Program.cs里面添加篩選器的方式稱為全局篩選器,所有控制器、操作都會受全局篩選器影響。還有一種篩選器實(shí)現(xiàn)方式是屬性篩選器,通過繼承屬性類然后將屬性標(biāo)簽放置在控制器或者操作上。
新建兩個(gè)屬性類MyAttributeFilter
用于Controller控制器類,MyOPAttributeFilter
用于操作方法上。
public class MyAttributeFilter: ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 控制器之后-篩選器屬性!");
}
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 控制器之前-篩選器屬性!");
}
}
public class MyOPAttributeFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 操作之后-篩選器屬性!");
}
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 操作之前-篩選器屬性!");
}
}
加上之前的全局篩選器,我們一共有三個(gè)作用域的篩選器,現(xiàn)在我們測試看看篩選器的執(zhí)行順序。
則可總結(jié)出不同作用域篩選器的執(zhí)行順序:
全局篩選器的 before 代碼。
控制器篩選器的 before 代碼。
操作方法篩選器的 before 代碼。
操作方法篩選器的 after 代碼。
控制器篩選器的 after 代碼。
全局篩選器的 after 代碼。
當(dāng)然可以通過 Order 屬性來確定執(zhí)行順序,在全局或者屬性篩選器里面設(shè)置 Order 值,值越小執(zhí)行優(yōu)先級越高。
四、篩選器依賴注入
可按類型或?qū)嵗砑雍Y選器。 如果添加實(shí)例,該實(shí)例將用于每個(gè)請求。
其中builder.Services.AddControllers(options => options.Filters.Add<MyActionFilter>())
即為按實(shí)例添加,該MyActionFilter
用于每個(gè)請求。
如果添加類型,則將激活該類型。 激活類型的篩選器意味著:第一種是為每個(gè)請求創(chuàng)建一個(gè)實(shí)例,第二種依賴關(guān)系注入 (DI) 將填充所有構(gòu)造函數(shù)依賴項(xiàng)。
上面位置我們是為每個(gè)請求創(chuàng)建一個(gè)實(shí)例,這樣的話無法使用依賴注入體系為我們自動(dòng)注入,因?yàn)橐驗(yàn)閷傩栽趹?yīng)用時(shí)必須提供自己的構(gòu)造函數(shù)參數(shù),該參數(shù)需要手動(dòng)指定。
比如我們想在操作方法的MyOPAttributeFilter
篩選屬性 注入IHostEnvironment
:
public class MyOPAttributeFilter : ActionFilterAttribute
{
IHostEnvironment hostEnvironment;
public MyOPAttributeFilter(IHostEnvironment _hostEnvironment)
{
hostEnvironment = _hostEnvironment;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 操作之后-篩選器屬性!");
}
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + " 操作之前-篩選器屬性!");
//打印環(huán)境變量
Console.WriteLine(hostEnvironment.EnvironmentName);
}
}
這個(gè)時(shí)候直接就報(bào)錯(cuò)提示需要參數(shù),而我們想的是通過依賴注入配置。
框架提供以下篩選器支持從 DI 提供的構(gòu)造函數(shù)依賴項(xiàng):
- ServiceFilterAttribute
- TypeFilterAttribute
- 在屬性上實(shí)現(xiàn) IFilterFactory。
TypeFilterAttribute:不會直接從 DI 容器解析其類型。Microsoft.Extensions.DependencyInjection.ObjectFactory 對類型進(jìn)行實(shí)例化,所以不需要先將MyOPAttributeFilter
加入容器,直接使用:
[TypeFilter(typeof(MyOPAttributeFilter))]
ServiceFilterAttribute
使用需要先將MyOPAttributeFilter
注入到容器,然后再使用。
以上就是關(guān)于AOP切面編程和篩選器的梳理,其他類型的篩選器和細(xì)節(jié)可查詢官方文檔:ASP.NET Core 中的篩選器