錯誤處理和狀態代碼
1)為什麼要標準化錯誤
單一錯誤合同加快了客戶調試,減少了虛假的重復並使RCA可播放。良好的系統:- 可以預見地編碼問題的類型,
- 向客戶提供有效的提示(接下來要做什麼),
- 防止內部零件泄漏,
- 與復古和冪等兼容。
2)設計原則
1.所有服務的一個錯誤模式(REST/GraphQL/gRPC/webhooks)。
2.清晰的retrae語義:哪些代碼是retra,哪些不是。
3.在寫操作上失效:優於4xx/5xx而不是安靜的非約束。
4.不泄漏:不披露SQL、堆棧、configs、內部ID。
5.跟蹤:始終返回'trace_id'/'correlation_id'。
6.消息本地化-可選,但代碼和「reason」保持穩定。
3)統一格式(問題詳細信息/JSON)
推薦的基本格式(與RFC 7807兼容):json
{
"type": "https://errors.example.com/auth/invalid-token",
"title": "Invalid access token",
"status": 401,
"code": "AUTH_INVALID_TOKEN",
"detail": "Token expired or signature invalid.",
"instance": "/api/v1/payments/12345",
"trace_id": "01HX3...ABC",
"hint": "Obtain a new token via OAuth2 refresh.",
"meta": {
"scope": "payments:write",
"policy": "deny-by-default"
}
}
說明:
- 「類型」是錯誤類的穩定URL ID。
- 「代碼」是短域機器代碼(版本之間穩定)。
- 「hint」-客戶端要做什麼(重播、更新令牌、更改設置)。
- 「meta」是安全的細節(沒有秘密和PII)。
4)狀態代碼映射(最低設置)
身份驗證/授權
400 Bad Request是結構驗證/方案。
401 Unauthorized-無/非驗證令牌。添加「WWW-Authenticate」。
403 Forbidden-已身份驗證,但沒有權利/政策被拒絕。
404 Not Found-在沒有權限的情況下掩蓋資源的存在。
409沖突是版本/狀態沖突(optimistic lock,等效性)。
451 Unavailable For Legal Reasons是一個合規性/管轄區塊。
限制和保護
408 Request Timeout-客戶端發送主體的速度太慢。
409/425 Too Early-第一0-RTT/TLS禁止早期重播。3.
429 Too Many Requests-帶有「Retry-After」和限制政策。
499 Client Closed Request-(在外圍/NGINX)客戶端斷開連接。
數據和業務規則
422 Unprocessable Content-業務驗證已通過該方案,但含義不正確。
423 Locked-資源鎖定(KYC review, AML freeze)。
409沖突是雙重發送,競賽,狀態限制(例如,「已經在處理中」)。
410 Gone-已刪除端口/資源(刪除已完成)。
服務器
500 Internal Server Error-未知錯誤;不要透露細節。
502 Bad Gateway-依賴性返回錯誤/滾動。
503 Service Unavailable-退化/計劃工作;添加「Retry-After」。
504 Gateway Timeout-依賴時間。
5)靜止和靜止語義
無法轉發:400/ 401/403/404/422(如果客戶沒有更改請求)。
可以轉發:408/429/5xx/ 425/499/504(帶有backoff+jitter)。
相似性:對於「POST」,請啟用「Idempotency-Key」(UUIDv4)。
對於重新運行沖突,請返回409,並帶有「hint:使用字符標識-鍵或GET狀態」。
返回保存的結果時添加「Idempotency-Replay: true」。
HTTP/1.1 429 Too Many Requests
Retry-After: 3
RateLimit-Limit: 50
RateLimit-Remaining: 0
RateLimit-Reset: 1730641030
6)入口驗證: 字段錯誤結構
對於400/422,請使用字段錯誤數組:json
{
"type": "https://errors.example.com/validation",
"title": "Validation failed",
"status": 422,
"code": "VALIDATION_ERROR",
"trace_id": "01HX4...XYZ",
"errors": [
{"field": "amount", "rule": "min", "message": "Must be >= 10"},
{"field": "currency", "rule": "enum", "message": "Unsupported currency"}
]
}
7)部分失敗(batch/partial failure)
在戰鬥結束中,不要在沒有結構的情況下將錯誤隱藏在200內。返回207 Multi-Status或200 c結果陣列,其中每個作業都有自己的狀態:json
{
"status": "partial",
"succeeded": 8,
"failed": 2,
"results": [
{"id": "op1", "status": 201},
{"id": "op2", "status": 422, "error": {"code":"VALIDATION_ERROR","detail":"..."}}
]
}
8)分割和「空白」答案
空白收藏是200,帶有「items:[]」,不是404。
頁面結尾為「next_page_token」。
不正確的令牌是400 code: PAGINATION_CURSOR_INVALID'。
9) Webhooks: 可靠的交付
簽署事件(HMAC)並在處理前檢查。
成功處理的答案是2xx(優於204)。
收件人的時間故障為5xx;發件人重復(指數backoff,jitter)。
通過「event_id」進行重復數據消除並保存結果(idempotent consumer)。
payload無效-400/422,沒有重復。
10)協議合規性(gRPC/GraphQL)
gRPC → HTTP:
`INVALID_ARGUMENT` → 400
`UNAUTHENTICATED` → 401
`PERMISSION_DENIED` → 403
`NOT_FOUND` → 404
`ALREADY_EXISTS` → 409
`FAILED_PRECONDITION` → 412/422
`RESOURCE_EXHAUSTED` → 429
`ABORTED` → 409
`UNAVAILABLE` → 503
`DEADLINE_EXCEEDED` → 504
GraphQL:在傳輸層中始終允許200個,但在「errors[]」中放置錯誤,並在標題/擴展中復制:json
{
"data": { "createPayment": null },
"errors": [{
"message": "Forbidden",
"extensions": { "code": "FORBIDDEN", "status": 403, "trace_id": "..." },
"path": ["createPayment"]
}]
}
對於嚴重錯誤,建議不要使用200,而是使用相應的HTTP代碼。
11)標題和提示客戶
「Retry-After」-秒/NTTR日期(429/503/425/408)。
「警告」是軟降解或去除(「199-Feature X已去除」)。
`Deprecation`, `Sunset`, `Link: <...>;rel=「deprecation」'-用於受控斷開連接。
「問題類型」(定制)-快速路由客戶端上的錯誤。
「X-Trace-Id」/「Correlation-Id」-鏈接徽標/Trays。
12)通訊安全
不要重復響應主體中的輸入秘密(令牌/簽名)。
偽裝PAN/PII('1234')。
對於401/403,請勿透露失敗的屬性。
對於404,而不是「資源輸出而不是您自己」-只是404。
13)錯誤的可觀察性
度量標準:- `http_errors_total{status, route, tenant}`
- 'error_classes_total {code}(來自主體的'code')
- 429, 5xx; 'p95'/'p99 'latency分配給錯誤答案
- 'retry_after_seconds_bucket'-重復提示直方圖
- 將響應鏈接到「trace_id」,存儲「code」,「type」,「status」,「route」,「tenant」,而不存儲PII。
- RPS> N下的'5xx_rate> X%'激增;
- 在關鍵路線上增加429條;
- 依賴關系中的「timeout/504」;
- 頻繁的409/偶數 →比賽的標誌。
14)示例
14.1,422(業務驗證)
json
{
"type": "https://errors.example.com/payments/limit-exceeded",
"title": "Limit exceeded",
"status": 422,
"code": "PAYMENT_LIMIT_EXCEEDED",
"detail": "Daily withdrawal limit reached for KYC1.",
"hint": "Increase limits after KYC2 or try tomorrow.",
"trace_id": "01J5...XYZ"
}
14.2 409(等容)
HTTP/1.1 409 Conflict
Idempotency-Replay: true
json
{
"type": "https://errors.example.com/idempotency/replay",
"title": "Duplicate request",
"status": 409,
"code": "IDEMPOTENT_REPLAY",
"detail": "A request with the same Idempotency-Key was already processed.",
"hint": "Reuse the same Idempotency-Key and GET the operation status."
}
14.3,429(限額)
json
{
"type":"https://errors.example.com/rate/too-many-requests",
"title":"Too many requests",
"status":429,
"code":"RATE_LIMITED",
"detail":"Per-key rate limit exceeded.",
"hint":"Retry after the time specified in Retry-After header."
}
15)反模式
返回200和主體中的錯誤文本。
在服務之間混合不同的錯誤格式。
在「詳細信息」中披露堆棧/SQL/表名/內部 URL。
使用「消息」代替穩定的「代碼」/「類型」。
在預期業務錯誤時返回500(例如,「資產負債表不足」)。
REST/GraphQL/gRPC之間的語義不一致。
16) iGaming/財務細節
KYC/AML/制裁的明確代碼是:「KYC_REQUIRED」,「KYC_REVIEW」,「AML_LOCK」,「SANCTION_BLOCKED」。
管轄權限制:451,措辭安全,沒有指定清單。
現金寫作交易:409/423在競爭和鎖定中,帶有重播窗口的「hint」。
玩家限額不變量:使用422違反負責任支付規則。
審計:不變的決策日誌(代碼、時間、演員、trace_id)。
17)準備就緒支票清單
- 單個JSON誤差圖,穩定的「類型」/「代碼」。
- Mapping HTTP ↔ gRPC/GraphQL是一致且有記錄的。
- retrae+「Retry-After」的語義;write的等效性。
- 偽裝PII/秘密;404用於隱藏資源。
- 錯誤和異常度量;以「trace_id」表示。
- Deprecate政策:「Deprecation」,「Sunset」,「Link」。
- 測試:negative/fuzz,版本沖突,依賴性下降,雙次提交。
- 海德客戶:回收和處理409/422/429/5xx。
18) TL;DR
將單個JSON錯誤格式c'type'/'code'/'trace_id'標準化,使用正確的HTTP代碼,區分驗證(400/422), (401/403/404)、沖突/等效性(409)和限制(429)。讓我們明確的「Retry-After」和「hint」,掩蓋敏感數據,用「trace_id」繪制錯誤,並在5xx/429/p99上構建變量。