错误响应设计是什么
错误响应要同时服务两类读者:机器要能稳定判断并分支处理,人要能看懂该做什么。通用做法是采用 Problem Details 结构(RFC 9457),用 type 标识错误类别、title 给出可读摘要、status 对应 HTTP 状态码、detail 描述本次具体情况、instance 指向本次请求的实例标识,便于用关联 ID 去日志与追踪里定位。字段级校验错误则需要结构化列出具体字段,前端才能准确高亮。
基本信息
| 标准结构 | type / title / status / detail / instance(Problem Details) |
|---|---|
| 内容协商 | 媒体类型 application/problem+json |
| 状态码 | 与结构一致:400 参数、401 未认证、403 无权限、409 冲突、422 校验失败、429 限流 |
| 关联标识 | instance 或 trace_id 与日志、追踪打通 |
| 安全边界 | 不返回堆栈、SQL、内部主机名与内部错误码 |
典型使用场景
- 表单提交失败:返回字段级错误列表,前端逐项高亮并给出修正提示
- 幂等与并发冲突:版本不匹配返回 409 或 412,客户端拉取最新数据后重试
- 限流与配额:返回 429 并在响应头给出重试建议时间
- 依赖故障:区分「请求有错」与「服务暂时不可用」,让调用方决定是否重试
- 第三方对接:错误码与结构稳定,集成方可以写出可靠的重试与告警逻辑
- 排查定位:凭 instance 在日志系统中检索同一次请求的完整上下文
响应示例
响应示例
{
"type": "https://errors.example.com/validation", // 错误类别,客户端按此分支处理
"title": "Validation failed", // 稳定摘要,不随单次请求变化
"status": 422, // 与 HTTP 状态码一致
"detail": "field 'email' is invalid", // 本次具体原因,可行动
"instance": "/requests/7f3c9a52", // 关联标识,用于排查
"errors": [ // 字段级错误清单
{"field": "email", "code": "invalid_format", "message": "邮箱格式不正确"}
]
}字段与澄清
| 字段 | 作用 | 常见问题 |
|---|---|---|
| type | 机器可判别的错误类别 | 直接写 URL 路径或随实现漂移,客户端无法稳定判断 |
| title | 稳定的可读摘要 | 把 detail 的内容写进 title,每次都在变 |
| status | 与 HTTP 状态码一致 | 结构与状态码不一致,网关与客户端处理冲突 |
| detail | 本次请求的具体原因 | 暴露内部实现细节或根本没有可行动信息 |
| instance | 本次请求的关联标识 | 缺失时无法把用户反馈与日志对上 |
错误信息分两套:给调用方的可行动提示与给内部的详细诊断。内部细节写日志并用关联标识串起来,不要把堆栈直接返回给调用方。
错误码一旦对外发布就属于契约:新增细分错误应并存原码或走版本策略,直接改语义会让客户端的重试与告警逻辑失效。