使用.NET 6開發TodoList應用(12)——實現ActionFilter


系列導航及源代碼

需求

Filter在.NET Web API項目開發中也是很重要的一個概念,它運行在執行MVC響應的Pipeline中執行,允許我們將一些可以在多個Action之間重用的邏輯抽取出來集中管理。雖然我們在上一篇使用.NET 6開發TodoList應用(11)——使用FluentValidation和MediatR實現接口請求驗證中演示了如何通過使用MediatR提供的IPipelineBehavior接口在CQRS的Handle方法執行前后插入可重用代碼,而本文所演示的Filters作用在Controller的Action執行或Action返回結果前后。

可以創建自定義Filters,用於處理應用程序中的橫切片關注點。 橫切片關注點的包括錯誤處理、緩存、配置、授權和日志記錄。 Filters可以避免重復代碼。

Filter的類型分為以下幾種:

  • Authorization Filters:最先運行,用於確定是否已針對請求為用戶授權。 如果請求未獲授權,Authorization Filters可以讓管道短路。

  • Resource Filters:授權后運行。OnResourceExecuting在Filter Pipeline的其余階段之前運行代碼。OnResourceExecuted在管道的其余階段完成之后運行代碼。可以用這個類型的Filter進行緩存和性能統計。

  • Action Filters:在調用操作方法之前和之后立即運行代碼。它可以更改傳遞到操作中的參數,也可以更改從操作返回的結果,當然如果什么都不更改僅作記錄也是可以的。

  • Exception Filters:在向響應正文寫入任何內容之前,對未經處理的異常應用全局策略。

  • Result Filters:在執行操作結果返回之前和之后運行代碼。 僅當操作方法成功執行時,它們才會被運行。

這五種Filters在Filter Pipeline中直觀的展現是這樣的:

image

而整個FIlter Pipeline在完整的Middleware Pipeline中的階段是這樣的:

image

在本文中,我們將演示Action Filters是如何在Controller的Action執行前后記錄請求和響應日志的。

目標

使用Action Filters進行接口日志記錄。

原理與思路

創建一個自定義的Action Filter,用於實現Controller的接口日志邏輯。

實現

Api新建文件夾Filters並創建類LogFilterAttribute:

  • LogFilterAttribute.cs
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace TodoList.Api.Filters;

public class LogFilterAttribute : IActionFilter
{
    private readonly ILogger<LogFilterAttribute> _logger;

    public LogFilterAttribute(ILogger<LogFilterAttribute> logger) => _logger = logger;

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var action = context.RouteData.Values["action"];
        var controller = context.RouteData.Values["controller"];
        // 獲取名稱包含Command的參數值
        var param = context.ActionArguments.SingleOrDefault(x => x.Value.ToString().Contains("Command")).Value;

        _logger.LogInformation($"Controller:{controller}, action: {action}, Incoming request: {JsonSerializer.Serialize(param)}");
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        var action = context.RouteData.Values["action"];
        var controller = context.RouteData.Values["controller"];
        // 需要先將Result轉換為ObjectResult類型才能拿到Value值
        var result = (ObjectResult)context.Result!;

        _logger.LogInformation($"Controller:{controller}, action: {action}, Executing response: {JsonSerializer.Serialize(result.Value)}");
    }
}

依賴注入:

  • Program.cs
builder.Services.AddScoped<LogFilterAttribute>();

在需要應用該Filter的Controller Action上添加屬性:

  • TodoListController.cs
[HttpPost]
[ServiceFilter(typeof(LogFilterAttribute))]
public async Task<ApiResponse<Domain.Entities.TodoList>> Create([FromBody] CreateTodoListCommand command)
{
    return ApiResponse<Domain.Entities.TodoList>.Success(await _mediator.Send(command));
}

驗證

啟動Api項目,執行創建TodoList的請求:

  • 請求
    image

  • 響應

    • 來自於OnActionExecuting的請求數據日志:

      image

      注意在我們上一篇文章中的Handling CreateTodoListCommand之前輸出的內容。

    • 以及來自於OnActionExecuted輸出的返回數據日志:

      image

一點擴展

關於Filter的主題還包含關於其作用域,優先級順序以及如何調整優先級等,其他主題像如何改寫響應,如何進行性能統計和緩存,我在這里暫時不做演示,可以參考微軟官方文檔:ASP.NET Core 中的篩選器,進行更多了解。

總結

在本文中我們通過一個很簡單的例子,演示了Action Filter的基本用法。至此我們關於請求中間件管道的討論先告一個段落,后面說到認證鑒權的時候我們還會回來討論這個主題。

從下一篇開始,我們集中來討論查詢操作中涉及的一些典型場景:包括分頁、排序、過濾、搜索等。

參考資料

  1. ASP.NET Core 中的篩選器


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM