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

XSpringSun發表於2023-12-18

一、篩選器

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

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

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

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

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

image

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

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

image

二、操作型篩選器

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

操作篩選器可以實現介面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裡面新增篩選器
    image

執行測試介面
image

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

上面直接在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() + "  操作之前-篩選器屬性!");
        }
    }

image

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

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

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

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

image

四、篩選器依賴注入

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

其中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);
        }
    }

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

image

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

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

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

[TypeFilter(typeof(MyOPAttributeFilter))]

image

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

image

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

相關文章