.net 溫故知新【16】:Asp.Net Core WebAPI 篩選器

2023-12-18 15:02:54

一、篩選器

通過使用篩選器可在請求處理管道中的特定階段之前或之後執行程式碼。

這即是我們經常聽到的面向切面程式設計AOP(Aspect Oriented Programming)技術,AOP通過預編譯方式和執行期間動態代理實現程式功能的統一維護的一種技術。

篩選器在 ASP.NET Core 操作呼叫管道(有時稱為篩選器管道)內執行。 篩選器管道在 ASP.NET Core 選擇了要執行的操作之後執行:

Asp.Net Core 關注的切面點 包括錯誤處理、快取、設定、授權和紀錄檔記錄篩選器,這個是說通過篩選器可以實現對以上關注點的一些操作。

在Asp.Net Core中有如下幾種型別的篩選器:

其中部分是內建篩選器,比如授權,響應快取已經幫我們內建進了框架,我們只需要設定即可使用;其他篩選器是可以自定義處理邏輯的。

下圖展示了篩選器型別在篩選器管道中的互動方式和執行順序:

二、操作型篩選器

第一部分主要是對篩選器的一個梳理,有些重點的提煉,詳情檢視檔案,因為檔案部分理解起來比較晦澀,比如關注點是關注點,知識說篩選器可以對這些關注點啟到作用,篩選器是固定的幾種,不要被檔案中的這種描述搞暈了,一會兒有這幾種,怎麼到下面又是另外幾種,要注意區分重點。

操作篩選器可以實現介面IActionFilter,在介面中有兩個方法,OnActionExecuting 在呼叫操作方法之前執行。 OnActionExecuted 在操作方法返回之後執行。

  • 先建WebAPI專案 WebAPI_Filter
  • 建一個 FilterController,並建立Get請求Test
namespace WebAPI_Filter.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class FilterController : ControllerBase
    {
        [HttpGet]
        public string Test()
        {
            return "測試Filter!";
        }
    }
}
  • 建立ActionFilter 篩選器
namespace WebAPI_Filter.Filter
{
    public class MyActionFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl()+ "  執行之後!");
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  執行之前!");
        }
    }
}
  • 在Program.cs裡面新增篩選器

執行測試介面

三、篩選器作用域和執行順序

上面直接在Program.cs裡面新增篩選器的方式稱為全域性篩選器,所有控制器、操作都會受全域性篩選器影響。還有一種篩選器實現方式是屬性篩選器,通過繼承屬性類然後將屬性標籤放置在控制器或者操作上。

新建兩個屬性類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() + "  操作之前-篩選器屬性!");
        }
    }

加上之前的全域性篩選器,我們一共有三個作用域的篩選器,現在我們測試看看篩選器的執行順序。

則可總結出不同作用域篩選器的執行順序:

全域性篩選器的 before 程式碼。
	控制器篩選器的 before 程式碼。
		操作方法篩選器的 before 程式碼。
		操作方法篩選器的 after 程式碼。
	控制器篩選器的 after 程式碼。
全域性篩選器的 after 程式碼。

當然可以通過 Order 屬性來確定執行順序,在全域性或者屬性篩選器裡面設定 Order 值,值越小執行優先順序越高。

四、篩選器依賴注入

可按型別或範例新增篩選器。 如果新增範例,該範例將用於每個請求。

其中builder.Services.AddControllers(options => options.Filters.Add<MyActionFilter>())即為按範例新增,該MyActionFilter用於每個請求。

如果新增型別,則將啟用該型別。 啟用型別的篩選器意味著:第一種是為每個請求建立一個範例,第二種依賴關係注入 (DI) 將填充所有建構函式依賴項。

上面位置我們是為每個請求建立一個範例,這樣的話無法使用依賴注入體系為我們自動注入,因為因為屬性在應用時必須提供自己的建構函式引數,該引數需要手動指定。

比如我們想在操作方法的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() + "  操作之前-篩選器屬性!");
            //列印環境變數
            Console.WriteLine(hostEnvironment.EnvironmentName);
        }
    }

這個時候直接就報錯提示需要引數,而我們想的是通過依賴注入設定。

框架提供以下篩選器支援從 DI 提供的建構函式依賴項:

  • ServiceFilterAttribute
  • TypeFilterAttribute
  • 在屬性上實現 IFilterFactory。

TypeFilterAttribute:不會直接從 DI 容器解析其型別。Microsoft.Extensions.DependencyInjection.ObjectFactory 對型別進行範例化,所以不需要先將MyOPAttributeFilter加入容器,直接使用:

[TypeFilter(typeof(MyOPAttributeFilter))]

ServiceFilterAttribute 使用需要先將MyOPAttributeFilter注入到容器,然後再使用。

以上就是關於AOP切面程式設計和篩選器的梳理,其他型別的篩選器和細節可查詢官方檔案:ASP.NET Core 中的篩選器