errors.md - maoxiaoyue/hypgo GitHub Wiki
pkg/errors — Typed Error Catalog
預定義結構化錯誤碼系統,讓 AI 和人都能一致理解和使用錯誤。
設計理念
傳統做法每個 handler 各自定義錯誤訊息,AI 無法預測回傳格式。Typed Error Catalog 將錯誤定義為全域常數,統一格式且可被 Manifest 收錄:
❌ c.JSON(404, map[string]string{"error": "user not found"}) ← 隨意格式
✅ errors.AbortWithAppError(c, errors.ErrUserNotFound.With("id", 42)) ← 結構化
快速上手
定義錯誤碼
package apperrors
import "github.com/maoxiaoyue/hypgo/pkg/errors"
var (
ErrUserNotFound = errors.Define("E1001", 404, "User not found", "users")
ErrUserExists = errors.Define("E1002", 409, "User already exists", "users")
ErrInvalidEmail = errors.Define("E1003", 422, "Invalid email format", "users")
)
在 handler 中使用
func getUser(c *hypcontext.Context) {
id := c.Param("id")
user, err := userService.FindByID(id)
if err != nil {
errors.AbortWithAppError(c, apperrors.ErrUserNotFound.With("id", id))
return
}
c.JSON(200, user)
}
回應格式
{
"code": "E1001",
"message": "User not found",
"details": {
"id": "42"
}
}
核心 API
Define — 定義錯誤
errors.Define(code, httpStatus, message, category) *AppError
自動註冊到全域 Catalog,通常在 package-level var 區塊中使用。
AppError 方法
// 附加上下文(回傳副本,原始不變)
err.With("field", "email")
err.WithDetail("reason", "already exists")
err.WithDetails(map[string]any{"a": 1, "b": 2})
err.WithMessage("Custom message")
// 轉為 JSON(給 HTTP response 用)
err.JSON() // → map[string]any{"code":"E1001","message":"...","details":{...}}
// 錯誤比對(只比 Code,忽略 Details)
errors.Is(err1, err2) // true if same code
Context 整合
// 中斷請求並回傳結構化錯誤
errors.AbortWithAppError(c, err)
// 回傳錯誤但不中斷 middleware 鏈
errors.RespondError(c, err)
Catalog 查詢
catalog := errors.GlobalCatalog()
// 查詢所有已定義的錯誤
all := catalog.All()
// 根據 code 查詢
err, ok := catalog.Get("E1001")
// 根據分類查詢
authErrors := catalog.ByCategory("auth")
不可變設計
AppError 的 With*() 方法永遠回傳副本,原始定義不會被修改:
original := errors.Define("E1", 404, "Not found", "general")
copy1 := original.With("id", 1)
copy2 := original.With("id", 2)
// original.Details == nil(未被修改)
// copy1.Details == {"id": 1}
// copy2.Details == {"id": 2}
預定義錯誤
| Code | HTTP | Message | Category |
|---|---|---|---|
| E0001 | 404 | Resource not found | general |
| E0002 | 400 | Bad request | general |
| E0003 | 500 | Internal server error | general |
| E0004 | 405 | Method not allowed | general |
| E1001 | 422 | Validation failed | validation |
| E1002 | 400 | Missing required field | validation |
| E1003 | 400 | Invalid format | validation |
| E2001 | 401 | Authentication required | auth |
| E2002 | 403 | Permission denied | auth |
| E2003 | 401 | Token expired | auth |
架構
pkg/errors/
├── catalog.go AppError、Define()、Catalog、預定義錯誤
├── context.go AbortWithAppError()、RespondError()
└── catalog_test.go 19 個單元測試
測試
go test ./pkg/errors/... -v