第七章:橫切關注點 (Cross-Cutting Concerns)
7.1 引言:什麼是橫切關注點?
在軟體開發中,有些功能是「無處不在」的,例如:
- 驗證:確保輸入資料合法。
- 授權:確保使用者有權執行操作。
- 日誌:記錄發生了什麼事。
- 例外處理:優雅地處理錯誤。
如果我們在每個方法中都重複寫這些程式碼,系統將變得難以維護。ABP 利用 AOP (Aspect-Oriented Programming) 技術,將這些關注點從業務邏輯中抽離出來,讓您專注於核心價值。
7.2 自動驗證 (Validation)
ABP 自動整合了 .NET Core 的驗證系統,並支援 FluentValidation。
1. DTO 驗證
最簡單的方式是使用 Data Annotations。
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.Contracts 或 Application 層中定義。
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
當您想要中斷流程並顯示訊息給使用者時,拋出此例外。
if (book.Price < 0)
{
throw new UserFriendlyException("價格不能為負數!");
}- HTTP Status: 403 Forbidden (預設) 或 500 Internal Server Error (視設定而定,通常建議用於業務錯誤)。
- 前端行為: ABP 的前端套件會自動攔截此錯誤並顯示 Toast 通知。
2. BusinessException (推薦)
用於領域層的業務規則驗證。它支援錯誤碼與本地化。
// 定義錯誤碼 (Domain.Shared)
public static class BookStoreErrorCodes
{
public const string BookAlreadyExists = "BookStore:001";
}
// 拋出例外 (Domain)
throw new BusinessException(BookStoreErrorCodes.BookAlreadyExists)
.WithData("Name", name);3. 錯誤回應格式
ABP 統一了 API 的錯誤回傳格式:
{
"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 中設定:
[ConnectionStringName("Default")]
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Book>(b =>
{
// ...
});
}
}註:在 ABP V9 中,預設會對所有 FullAuditedAggregateRoot 啟用審計。若要針對特定實體啟用或停用,可在 Module 的 ConfigureServices 中設定 AbpAuditingOptions。
Configure<AbpAuditingOptions>(options =>
{
options.EntityHistorySelectors.AddAllEntities(); // 啟用所有實體的歷史紀錄 (慎用,資料量大)
});7.5 權限與授權 (Authorization)
ABP 的權限系統基於 ASP.NET Core Policy,但提供了更細粒度的控制。
1. 定義權限 (Permission Definition)
在 Application.Contracts 層的 PermissionDefinitionProvider 中定義。
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 (推薦)
[Authorize("BookStore.Books.Create")]
public async Task CreateAsync(CreateBookDto input)
{
// ...
}方式 B:IAuthorizationService
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 模組。
參考資源: