4. OpenAPI 中定义错误响应:在 paths 中定义 4xx/5xx 响应,使用 $ref 引用 components/schemas
在 OpenAPI 规范中,仅定义成功的 2xx 响应是不够的。一个健壮的 API 必须明确声明所有可能的错误场景,包括客户端错误(4xx)和服务器错误(5xx)。本章将讲解如何在 paths 中为每个操作定义标准化的错误响应,并通过 $ref 引用 components/schemas 中的复用模型,实现错误码的断言与通信机制验证。
4.1 错误响应的标准结构
每个错误响应在 OpenAPI 中都是一个完整的 response 对象,包含 description 和 content。为了确保所有错误响应格式统一,我们将其定义在 components/responses 中,然后在 paths 中通过 $ref 引用。
| 响应码 | 语义 | 典型场景 |
|---|---|---|
| 400 Bad Request | 请求参数校验失败 | 缺少必填字段、字段类型错误、格式不合法 |
| 401 Unauthorized | 身份认证失败 | Token 过期、无效凭证、未携带认证头 |
| 403 Forbidden | 权限不足 | 用户无操作权限、资源访问受限 |
| 404 Not Found | 资源不存在 | 请求路径错误、资源 ID 无效 |
| 409 Conflict | 资源冲突 | 唯一键重复、版本冲突、状态不允许操作 |
| 422 Unprocessable Entity | 业务逻辑校验失败 | 业务规则违反、依赖条件不满足 |
| 429 Too Many Requests | 请求频率超限 | API 限流触发 |
| 500 Internal Server Error | 服务器内部错误 | 未捕获异常、数据库连接失败 |
| 502 Bad Gateway | 上游服务不可用 | 网关代理超时、后端服务宕机 |
| 503 Service Unavailable | 服务暂时不可用 | 系统维护、过载保护 |
4.2 定义可复用的错误响应 Schema
首先在 components/schemas 中定义统一的错误响应体结构。所有错误响应共享相同的 JSON 格式,包含 code、message、request_id 和可选的 details 字段。
components:
schemas:
ApiError:
type: object
required:
- code
- message
- request_id
properties:
code:
type: string
description: "业务错误码,如 'USER_NOT_FOUND'"
example: "INVALID_PARAMETER"
message:
type: string
description: "人类可读的错误描述"
example: "请求参数 'email' 格式不正确"
request_id:
type: string
format: uuid
description: "请求唯一标识,用于日志追踪"
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
details:
type: array
description: "字段级错误详情(可选)"
items:
type: object
properties:
field:
type: string
example: "email"
issue:
type: string
example: "must be a valid email address"
ValidationError:
allOf:
- $ref: '#/components/schemas/ApiError'
- type: object
required:
- details
properties:
code:
example: "VALIDATION_FAILED"
message:
example: "请求参数校验失败,请检查输入"
4.3 在 components/responses 中定义命名响应
将常用的错误响应封装为命名响应,方便在多个路径中复用。每个响应引用上面定义的 Schema,并设置对应的 description。
components:
responses:
BadRequest:
description: "请求参数错误"
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
Unauthorized:
description: "身份认证失败"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
NotFound:
description: "请求的资源不存在"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
Conflict:
description: "资源冲突"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
InternalServerError:
description: "服务器内部错误"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
4.4 在 paths 中引用错误响应
在具体的 API 路径操作中,通过 $ref 引用上述命名响应。每个操作除了定义成功的 2xx 响应外,必须列出所有可能返回的错误状态码及其对应的响应引用。
paths:
/users/{userId}:
get:
summary: "获取用户信息"
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: "成功返回用户信息"
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
put:
summary: "更新用户信息"
responses:
'200':
description: "更新成功"
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
关键实践: 每个操作必须显式声明所有可能返回的 4xx/5xx 状态码。遗漏错误响应定义会导致生成的客户端 SDK 无法正确处理异常情况,破坏通信机制的健壮性。
4.5 错误响应引用关系图
下图展示了从 paths 到 components/responses 再到 components/schemas 的完整引用链路,以及错误响应在 API 通信中的位置。
4.6 错误响应定义的最佳实践
- 统一错误体结构:所有错误响应使用相同的 JSON 结构,包含
code、message、request_id,便于客户端统一解析。 - 业务错误码 vs HTTP 状态码:HTTP 状态码表示大类错误(4xx/5xx),业务错误码
code字段提供更细粒度的错误标识,如USER_NOT_FOUND、INSUFFICIENT_BALANCE。 - 必选字段声明:在 Schema 中使用
required强制要求code、message、request_id必须返回,确保断言可依赖。 - 避免重复定义:所有错误响应在
components/responses中定义一次,多个路径通过$ref引用,减少维护成本。 - 覆盖所有错误路径:每个操作必须声明所有可能返回的 4xx/5xx 状态码,遗漏定义会导致文档不完整,测试无法覆盖异常场景。
提示: 在测试框架中,可以编写一个通用的错误响应断言函数,接收
code 和 message 预期值,自动校验响应状态码、JSON 结构和字段内容。结合 OpenAPI 文档,实现自动化错误码断言通信机制验证。
4.7 错误响应定义示例:完整路径
以下是一个完整的路径定义示例,展示了如何为 POST /orders 操作声明所有可能的错误响应。
paths:
/orders:
post:
summary: "创建订单"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'201':
description: "订单创建成功"
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'422':
description: "业务校验失败"
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
'429':
description: "请求频率超限"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'500':
$ref: '#/components/responses/InternalServerError'
'503':
description: "服务暂时不可用"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
注意: 不要将错误响应定义在
paths 内部重复编写。始终使用 $ref 引用 components/responses 中的命名响应。这不仅是规范要求,更是保证文档一致性和可维护性的关键。