Skip to content

第七章:橫切關注點 (Cross-Cutting Concerns)

7.1 引言:什麼是橫切關注點?

在軟體開發中,有些功能是「無處不在」的,例如:

  • 驗證:確保輸入資料合法。
  • 授權:確保使用者有權執行操作。
  • 日誌:記錄發生了什麼事。
  • 例外處理:優雅地處理錯誤。

如果我們在每個方法中都重複寫這些程式碼,系統將變得難以維護。ABP 利用 AOP (Aspect-Oriented Programming) 技術,將這些關注點從業務邏輯中抽離出來,讓您專注於核心價值。


7.2 自動驗證 (Validation)

ABP 自動整合了 .NET Core 的驗證系統,並支援 FluentValidation

1. DTO 驗證

最簡單的方式是使用 Data Annotations。

csharp
public class CreateBookDto
{
    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    [Range(0, 1000)]
    public decimal Price { get; set; }
}

2. 使用 FluentValidation (推薦)

對於複雜的驗證邏輯,FluentValidation 是更好的選擇。

步驟 1:安裝套件 通常 Volo.Abp.FluentValidation 已經包含在啟動範本中。

步驟 2:定義驗證器Application.ContractsApplication 層中定義。

csharp
public class CreateBookDtoValidator : AbstractValidator<CreateBookDto>
{
    public CreateBookDtoValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty()
            .MaximumLength(128)
            .WithMessage("書名不能為空且長度不能超過 128 字元");

        RuleFor(x => x.Price)
            .GreaterThanOrEqualTo(0);

        // 自訂邏輯
        RuleFor(x => x.Name).Must(name => !name.Contains("禁書"))
            .WithMessage("不能新增禁書");
    }
}

ABP 會自動掃描並註冊這些驗證器。當 AppService 被呼叫時,攔截器會自動執行驗證,若失敗則拋出 AbpValidationException,並回傳 400 Bad Request


7.3 例外處理 (Exception Handling)

ABP 提供了一套標準的例外處理機制,將後端例外轉換為友善的 HTTP 回應。

1. UserFriendlyException

當您想要中斷流程並顯示訊息給使用者時,拋出此例外。

csharp
if (book.Price < 0)
{
    throw new UserFriendlyException("價格不能為負數!");
}
  • HTTP Status: 403 Forbidden (預設) 或 500 Internal Server Error (視設定而定,通常建議用於業務錯誤)。
  • 前端行為: ABP 的前端套件會自動攔截此錯誤並顯示 Toast 通知。

2. BusinessException (推薦)

用於領域層的業務規則驗證。它支援錯誤碼與本地化。

csharp
// 定義錯誤碼 (Domain.Shared)
public static class BookStoreErrorCodes
{
    public const string BookAlreadyExists = "BookStore:001";
}

// 拋出例外 (Domain)
throw new BusinessException(BookStoreErrorCodes.BookAlreadyExists)
    .WithData("Name", name);

3. 錯誤回應格式

ABP 統一了 API 的錯誤回傳格式:

json
{
  "error": {
    "code": "BookStore:001",
    "message": "書籍 'Harry Potter' 已經存在。",
    "details": "...",
    "validationErrors": null
  }
}

7.4 審計日誌 (Audit Logging)

審計日誌對於企業應用至關重要,它記錄了「誰」在「什麼時候」做了「什麼事」。

1. 自動記錄

預設情況下,ABP 會自動記錄所有 Application Service 的方法呼叫,包括:

  • 使用者 ID
  • 執行時間
  • 方法參數 (JSON 格式)
  • 執行時長
  • 客戶端 IP / 瀏覽器資訊
  • 例外資訊 (如果有)

2. 實體變更紀錄 (Entity Change Logging)

若實體繼承自 FullAuditedAggregateRoot,且啟用了實體變更追蹤,ABP 還會記錄資料庫層面的變更 (Insert/Update/Delete) 以及變更前後的屬性值。

啟用方式: 在 DbContext 中設定:

csharp
[ConnectionStringName("Default")]
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<Book>(b =>
        {
            // ...
        });
    }
}

註:在 ABP V9 中,預設會對所有 FullAuditedAggregateRoot 啟用審計。若要針對特定實體啟用或停用,可在 ModuleConfigureServices 中設定 AbpAuditingOptions

csharp
Configure<AbpAuditingOptions>(options =>
{
    options.EntityHistorySelectors.AddAllEntities(); // 啟用所有實體的歷史紀錄 (慎用,資料量大)
});

7.5 權限與授權 (Authorization)

ABP 的權限系統基於 ASP.NET Core Policy,但提供了更細粒度的控制。

1. 定義權限 (Permission Definition)

Application.Contracts 層的 PermissionDefinitionProvider 中定義。

csharp
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
    public override void Define(IPermissionDefinitionContext context)
    {
        var myGroup = context.AddGroup("BookStore");

        var booksPermission = myGroup.AddPermission("BookStore.Books");
        booksPermission.AddChild("BookStore.Books.Create");
        booksPermission.AddChild("BookStore.Books.Delete");
    }
}

2. 檢查權限

方式 A:Attribute (推薦)

csharp
[Authorize("BookStore.Books.Create")]
public async Task CreateAsync(CreateBookDto input)
{
    // ...
}

方式 B:IAuthorizationService

csharp
public async Task UpdateAsync(Guid id, UpdateBookDto input)
{
    if (!await AuthorizationService.IsGrantedAsync("BookStore.Books.Edit"))
    {
        throw new AbpAuthorizationException();
    }
    // ...
}

3. 權限管理 UI

如果您使用了 ABP 的 Identity 模組,登入 Admin 帳號後,您可以在「角色管理」或「使用者管理」頁面中,勾選上述定義的權限。這是完全動態的,無需重啟應用程式。


7.6 習題

概念題(易)⭐

習題 1:解釋橫切關注(Cross-Cutting Concerns)的概念,列舉三個例子。

請說明:

  • 橫切關注的定義
  • 為什麼需要將其分離
  • ABP 如何處理橫切關注
  • 列舉至少 3 個實際例子

習題 2:Permission 與 Role 的區別?在 ABP 中如何定義與檢查?

請說明:

  • Permission 和 Role 的概念差異
  • 在 ABP 中如何定義 Permission
  • 如何檢查 Permission(Attribute 和程式碼方式)
  • 權限繼承機制

計算/練習題(中)⭐⭐

習題 3:實作一個 Permission 檢查機制,包含父子權限關係與繼承。

要求:

  • 定義權限樹結構(至少 3 層)
  • 實作權限檢查邏輯
  • 處理權限繼承
  • 提供測試案例

習題 4:實作日誌與審計,記錄所有 Book 的 CRUD 操作。

要求:

  • 啟用實體變更追蹤
  • 自訂審計日誌格式
  • 實作查詢審計日誌的 API
  • 提供日誌分析功能

實作題(較難)⭐⭐⭐

習題 5 & 6:綜合實作

實作一個完整的權限與審計系統:

  • 定義完整的權限樹(至少 10 個權限)
  • 實作基於角色的權限分配
  • 記錄所有敏感操作的審計日誌
  • 提供審計日誌查詢與匯出功能
  • 實作權限變更的通知機制

習題解答:請參考 content/solutions/ch07-solutions.md


7.7 總結

本章展示了 ABP 如何自動化處理繁瑣的橫切關注點。

  • 驗證:使用 FluentValidation 保持 DTO 乾淨。
  • 例外:使用 BusinessException 統一錯誤處理。
  • 審計:自動記錄操作與資料變更。
  • 權限:細粒度的動態權限控制。

這些機制共同構成了一個健壯的企業級應用基礎。下一章,我們將探討 ABP 的 開源特色與社群服務功能,包括多語言、自動 API 與 EasyAbp 模組。


參考資源

Released under the MIT License.