3. 错误码设计原则:HTTP状态码与业务错误码的区别,RESTful API错误响应体设计规范

3.1 核心概念辨析:HTTP状态码 vs 业务错误码

在RESTful API设计中,错误码体系分为两个层次:传输层状态码(HTTP Status Code)与应用层业务码(Business Error Code)。二者职责不同,不能混用,也不能互相替代。

维度 HTTP状态码 业务错误码
所属层次 传输层(HTTP协议) 应用层(业务逻辑)
语义范围 通用、协议级(如404表示资源不存在) 特定、业务级(如"USER_ACCOUNT_LOCKED")
粒度 粗粒度(约60个标准码) 细粒度(可定义数千个)
客户端处理方式 决定重试、重定向、降级 决定展示文案、补偿逻辑、跳转路径
是否可扩展 不建议自定义(违反RFC) 完全由API设计者定义
典型示例 400 Bad Request "INVALID_PARAMETER_FORMAT"
核心原则:HTTP状态码只描述"请求能否被正常处理";业务错误码描述"处理结果的具体业务含义"。一个400状态码可以对应数十种不同的业务错误码。

3.2 分层映射关系

HTTP状态码与业务错误码之间存在一对多的映射关系。设计时应遵循以下映射规则:

  • 2xx 成功族:业务错误码为"SUCCESS"或"OK",或省略不传。
  • 4xx 客户端错误族:对应参数校验失败、鉴权失败、资源冲突、请求被限流等业务场景。
  • 5xx 服务端错误族:对应内部异常、依赖超时、数据不一致等不可控场景。
HTTP状态码与业务错误码分层映射关系 HTTP 状态码(传输层) 200 OK | 201 Created | 400 Bad Request | 401 Unauthorized | 403 Forbidden | 404 Not Found | 409 Conflict | 429 Too Many Requests | 500 Internal Server Error 一对多映射:一个HTTP状态码 → 多个业务错误码 业务错误码(应用层) USER_NOT_FOUND | INSUFFICIENT_BALANCE | ORDER_STATUS_CONFLICT | RATE_LIMIT_EXCEEDED 400 Bad Request INVALID_EMAIL_FORMAT MISSING_REQUIRED_FIELD 401 Unauthorized TOKEN_EXPIRED INVALID_CREDENTIALS 409 Conflict DUPLICATE_ENTRY VERSION_MISMATCH

3.3 标准错误响应体设计规范

一个结构清晰、可扩展的错误响应体是API契约的核心。推荐遵循 RFC 7807 Problem Details 规范,并结合业务需求进行适度扩展。

3.3.1 最小必要字段

字段名 类型 必填 说明
code string 是 业务错误码,如 INSUFFICIENT_BALANCE
message string 是 人类可读的错误描述,用于日志和调试
status integer 是 HTTP状态码,方便客户端快速判断
trace_id string 推荐 请求追踪ID,用于关联日志和排查问题

3.3.2 推荐扩展字段

字段名 类型 说明
details array 字段级校验错误列表,每个元素包含 field、reason、rejected_value
documentation_url string 指向该错误码的详细文档链接
retry_after integer 限流或临时性错误时,建议客户端等待的秒数

3.3.3 完整响应体示例

{
  "status": 400,
  "code": "INVALID_PARAMETER",
  "message": "请求参数校验失败,请检查输入",
  "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "details": [
    {
      "field": "email",
      "reason": "邮箱格式不正确",
      "rejected_value": "user@invalid"
    },
    {
      "field": "age",
      "reason": "年龄必须在18-120之间",
      "rejected_value": 15
    }
  ],
  "documentation_url": "https://api.example.com/docs/errors/INVALID_PARAMETER"
}
设计建议:错误响应体中的 message 字段应面向开发者,而非最终用户。面向用户的错误提示应在前端根据 code 字段进行国际化映射。

3.4 错误码命名规范

业务错误码的命名应具备自解释性和可搜索性,推荐采用以下约定:

  • 全大写 + 下划线:如 USER_NOT_FOUND、ORDER_ALREADY_PAID
  • 前缀分组:按模块或资源类型加前缀,如 USER_、ORDER_、PAYMENT_
  • 动词 + 名词:描述错误性质,如 MISSING_REQUIRED_FIELD、EXCEEDED_RATE_LIMIT
  • 避免数字编码:不要使用纯数字错误码(如 E1001),降低可读性

3.5 常见设计反模式

反模式1:将业务错误码直接映射为HTTP状态码,如用 499 表示"用户余额不足"。这违反了HTTP协议语义,且客户端框架可能无法正确处理非标准状态码。
反模式2:所有错误统一返回 200 OK,仅在响应体中用 code 区分。这导致客户端无法通过状态码快速判断请求是否成功,增加了处理复杂度。
反模式3:错误响应体结构不统一,不同接口返回不同字段名(如一个接口用 error_code,另一个用 errCode)。这破坏了API契约的一致性。

3.6 OpenAPI 中的错误响应定义

在OpenAPI 3.0规范中,应使用 oneOf 或 discriminator 来定义统一的错误响应模型,避免在每个操作中重复定义。

components:
  schemas:
    ApiError:
      type: object
      required:
        - status
        - code
        - message
      properties:
        status:
          type: integer
          description: "HTTP状态码"
        code:
          type: string
          description: "业务错误码"
        message:
          type: string
          description: "错误描述"
        trace_id:
          type: string
          format: uuid
          description: "请求追踪ID"
        details:
          type: array
          items:
            $ref: '#/components/schemas/FieldError'
    FieldError:
      type: object
      properties:
        field:
          type: string
        reason:
          type: string
        rejected_value:
          type: string

paths:
  /api/users/{id}:
    get:
      responses:
        '200':
          description: "成功"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '404':
          description: "用户不存在"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        'default':
          description: "其他错误"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'

3.7 错误码生命周期管理

业务错误码是API契约的一部分,应纳入版本管理。建议遵循以下生命周期策略:

业务错误码生命周期管理 ① 设计 定义code、message 写入OpenAPI文档 ② 实现 服务端抛出错误码 编写断言测试 ③ 发布 生成API变更日志 通知客户端团队 ④ 废弃 标记deprecated 移除 关键规则:错误码一旦发布,不得修改其语义 如需调整,应新增错误码并废弃旧码,保留至少一个大版本周期 废弃的错误码应在OpenAPI文档中标注 deprecated: true

3.8 本章小结

  • HTTP状态码与业务错误码是两个独立层次的标识,前者描述传输结果,后者描述业务语义。
  • 错误响应体应包含 status、code、message 三个必填字段,并推荐加入 trace_id 和 details。
  • 业务错误码命名应自解释、可搜索、带前缀分组,避免纯数字编码。
  • 在OpenAPI文档中,使用 $ref 引用统一的 ApiError schema,确保所有接口的错误响应结构一致。
  • 错误码应纳入生命周期管理,废弃时需在文档中标注并保留兼容期。