錯誤碼 × HTTP 對照表
這份文件專門整理 infra-core/errors 的「錯誤碼 → HTTP Status」對照,並提供實務範例。
錯誤系統採用 8 碼格式 SSCCCDDD:
SS = Scope(服務/模組,兩位數)
CCC = Category(類別,三位數,影響 HTTP 狀態)
DDD = Detail(細節,三位數,自定義業務碼)
例如:10101000 → Scope=10、Category=101(InputInvalidFormat)、Detail=000。
目錄
1) 快速查表(依類別整理)
A. Input(Category 1xx)
| Category 常數 |
說明 |
HTTP |
原因/說明 |
InputInvalidFormat (101) |
無效格式 |
400 Bad Request |
格式不符、缺欄位、型別錯。 |
InputNotValidImplementation (102) |
非有效實作 |
422 Unprocessable Entity |
語意正確但無法處理。 |
InputInvalidRange (103) |
無效範圍 |
422 Unprocessable Entity |
值超域、邊界條件不合。 |
B. DB(Category 2xx)
| Category 常數 |
說明 |
HTTP |
原因/說明 |
DBError (201) |
資料庫一般錯誤 |
500 Internal Server Error |
後端故障/不可預期。 |
DBDataConvert (202) |
資料轉換錯誤 |
422 Unprocessable Entity |
可修正的資料問題(格式/型別轉換失敗)。 |
DBDuplicate (203) |
資料重複 |
409 Conflict |
唯一鍵衝突、重複建立。 |
C. Resource(Category 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. Auth(Category 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. System(Category 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. PubSub(Category 7xx)
| Category 常數 |
說明 |
HTTP |
原因/說明 |
PSuPublish (701) |
發佈失敗 |
502 Bad Gateway |
中介或外部匯流排錯誤。 |
PSuConsume (702) |
消費失敗 |
502 Bad Gateway |
同上。 |
PSuTooLarge (703) |
訊息過大 |
413 Payload Too Large |
封包大小超限。 |
G. Service(Category 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