backend/pkg/library/errors/README.md

7.3 KiB
Raw Blame History

錯誤碼 × HTTP 對照表

這份文件專門整理 infra-core/errors 的「錯誤碼 → HTTP Status」對照並提供實務範例
錯誤系統採用 8 碼格式 SSCCCDDD

  • SS = Scope服務/模組,兩位數)
  • CCC = Category類別三位數影響 HTTP 狀態)
  • DDD = Detail細節三位數自定義業務碼

例如:10101000 → Scope=10、Category=101InputInvalidFormat、Detail=000。

目錄


1) 快速查表(依類別整理)

A. InputCategory 1xx

Category 常數 說明 HTTP 原因/說明
InputInvalidFormat (101) 無效格式 400 Bad Request 格式不符、缺欄位、型別錯。
InputNotValidImplementation (102) 非有效實作 422 Unprocessable Entity 語意正確但無法處理。
InputInvalidRange (103) 無效範圍 422 Unprocessable Entity 值超域、邊界條件不合。

B. DBCategory 2xx

Category 常數 說明 HTTP 原因/說明
DBError (201) 資料庫一般錯誤 500 Internal Server Error 後端故障/不可預期。
DBDataConvert (202) 資料轉換錯誤 422 Unprocessable Entity 可修正的資料問題(格式/型別轉換失敗)。
DBDuplicate (203) 資料重複 409 Conflict 唯一鍵衝突、重複建立。

C. ResourceCategory 3xx

Category 常數 說明 HTTP 原因/說明
ResNotFound (301) 資源未找到 404 Not Found 目標不存在/無此 ID。
ResInvalidFormat (302) 無效資源格式 422 Unprocessable Entity 表示層/Schema 不符。
ResAlreadyExist (303) 資源已存在 409 Conflict 重複建立/命名衝突。
ResInsufficient (304) 資源不足 400 Bad Request 數量/容量不足(用戶可改參數再試)。
ResInsufficientPerm (305) 權限不足 403 Forbidden 已驗證但無權限。
ResInvalidMeasureID (306) 無效測量ID 400 Bad Request ID 本身不合法。
ResExpired (307) 資源過期 410 Gone 已不可用(可於上層補 Location
ResMigrated (308) 資源已遷移 410 Gone 同上,如需導引請於上層處理。
ResInvalidState (309) 無效狀態 409 Conflict 當前狀態不允許此操作。
ResInsufficientQuota (310) 配額不足 429 Too Many Requests 達配額/速率限制。
ResMultiOwner (311) 多所有者 409 Conflict 所有權歧異造成衝突。

D. AuthCategory 5xx

Category 常數 說明 HTTP 原因/說明
AuthUnauthorized (501) 未授權/未驗證 401 Unauthorized 缺 Token、無效 Token。
AuthExpired (502) 授權過期 401 Unauthorized Token 過期或時效失效。
AuthInvalidPosixTime (503) 無效 POSIX 時間 401 Unauthorized 時戳異常導致驗簽失敗。
AuthSigPayloadMismatch (504) 簽名與載荷不符 401 Unauthorized 驗簽失敗。
AuthForbidden (505) 禁止存取 403 Forbidden 已驗證但沒有操作權限。

E. SystemCategory 6xx

Category 常數 說明 HTTP 原因/說明
SysInternal (601) 系統內部錯誤 500 Internal Server Error 未預期的系統錯。
SysMaintain (602) 系統維護中 503 Service Unavailable 維護/停機。
SysTimeout (603) 系統超時 504 Gateway Timeout 下游/處理逾時。
SysTooManyRequest (604) 請求過多 429 Too Many Requests 節流/限流。

F. PubSubCategory 7xx

Category 常數 說明 HTTP 原因/說明
PSuPublish (701) 發佈失敗 502 Bad Gateway 中介或外部匯流排錯誤。
PSuConsume (702) 消費失敗 502 Bad Gateway 同上。
PSuTooLarge (703) 訊息過大 413 Payload Too Large 封包大小超限。

G. ServiceCategory 8xx

Category 常數 說明 HTTP 原因/說明
SvcInternal (801) 服務內部錯誤 500 Internal Server Error 非基礎設施層的內錯。
SvcThirdParty (802) 第三方失敗 502 Bad Gateway 呼叫外部服務失敗。
SvcHTTP400 (803) 明確指派 400 400 Bad Request 自行指定。
SvcMaintenance (804) 服務維護中 503 Service Unavailable 模組級維運中。

2) 使用範例

2.1 在 Handler 中回傳錯誤

import (
    "net/http"
    errs "gitlab.supermicro.com/infra/infra-core/errors"
    "gitlab.supermicro.com/infra/infra-core/errors/code"
)

func init() {
    errs.Scope = code.Gateway // 設定當前服務的 Scope
}

func GetUser(w http.ResponseWriter, r *http.Request) error {
    id := r.URL.Query().Get("id")
    if id == "" {
        return errs.InputInvalidFormatError("缺少參數: id") // 現在是 8 位碼
    }

    u, err := repo.Find(r.Context(), id)
    switch {
    case errors.Is(err, repo.ErrNotFound):
        return errs.ResNotFoundError("user", id)
    case err != nil:
        return errs.DBErrorError("查詢使用者失敗").Wrap(err) // Wrap 內部錯誤
    }

    // … 寫入回應
    return nil
}

// 統一寫出 HTTP 錯誤
func writeHTTP(w http.ResponseWriter, e *errs.Error) {
    http.Error(w, e.Error(), e.HTTPStatus())
}

2.2 取出 Wrap 的內部錯誤

if internal := e.Unwrap(); internal != nil {
    log.Error("Internal error: ", internal)
}

2.3 搭配日誌裝飾器(WithLog / WithLogWrap

log := logger.WithFields(errs.LogField{Key: "req_id", Val: rid})

if badInput {
    return errs.WithLog(log, nil, errs.InputInvalidFormatError, "email 無效")
}

if err := repo.Save(ctx, u); err != nil {
    return errs.WithLogWrap(
        log,
        []errs.LogField{{Key: "entity", Val: "user"}, {Key: "op", Val: "save"}},
        errs.DBErrorError,
        err,
        "儲存失敗",
    )
}

2.4 只知道 Category+Detail 的動態場景(EL / ELWrap

// 依流程動態產生
return errs.EL(log, nil, code.SysTimeout, 123, "下游逾時") // 自定義 detail=123

// 或需保留 cause
return errs.ELWrap(log, nil, code.SvcThirdParty, 456, err, "金流商失敗")

2.5 gRPC 互通

// 由 *errs.Error 轉為 gRPC status
st := e.GRPCStatus() // *status.Status

// 客戶端收到 gRPC error → 轉回 *errs.Error
e := errs.FromGRPCError(grpcErr)
fmt.Println(e.DisplayCode(), e.Error()) // e.g., "10101000" "error msg"

2.6 從 8 碼反解(FromCode

e := errs.FromCode(10101000) // 10101000
fmt.Println(e.Scope(), e.Category(), e.Detail()) // 10, 101, 000