2. API 契约基础:契约的定义、作用与核心要素
在契约驱动开发(CDD)中,API 契约是服务提供者与消费者之间的一份技术性、可执行的协议。它精确描述了双方交互的格式、行为与约束,是团队并行开发、自动化测试与持续交付的基石。
2.1 什么是 API 契约?
API 契约是一份机器可读的规范文档,它明确声明了:
- 端点(Endpoint):资源路径与 HTTP 方法。
- 请求结构:Headers、Query Parameters、Request Body 的格式与类型。
- 响应结构:成功与失败时的 Body 结构、Headers。
- 状态码:每个操作对应的 HTTP 状态码语义。
- 错误格式:统一的错误响应结构。
核心原则:契约是“硬规则”,而非“软文档”。一旦签署,双方必须严格遵守;任何变更都需要通过版本管理协商。
2.2 契约的核心作用
| 作用维度 | 具体说明 | 敏捷价值 |
|---|---|---|
| 并行开发 | 前端/客户端可基于契约 Mock 服务端,无需等待后端实现完成。 | 缩短 Feature 交付周期 30%-50% |
| 自动化测试 | 契约可作为消费者驱动契约测试(CDC)的输入,验证双方是否遵守约定。 | 提前发现集成问题,减少回归 Bug |
| 文档即代码 | 契约自动生成交互文档(如 Swagger UI),保持文档与实现同步。 | 消除文档滞后,降低沟通成本 |
| 变更管理 | 通过契约版本化与兼容性检查,安全地演进 API。 | 支持持续重构,降低破坏性变更风险 |
2.3 契约的核心要素
一个完整的 API 契约必须包含以下四个核心要素。我们以 OpenAPI 3.0 规范为例进行说明。
2.3.1 请求结构(Request Structure)
请求结构定义了客户端如何调用 API,包括:
- 路径参数:如
/users/{userId}中的userId。 - 查询参数:如
?page=1&size=20。 - 请求头:如
Authorization、Content-Type。 - 请求体:JSON/XML 结构,包含字段名、类型、是否必填。
# OpenAPI 3.0 请求结构示例
paths:
/orders:
post:
summary: 创建订单
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- productId
- quantity
properties:
productId:
type: string
description: 商品 ID
quantity:
type: integer
minimum: 1
description: 数量
couponCode:
type: string
description: 优惠券码(可选)
2.3.2 响应结构(Response Structure)
响应结构定义了服务端返回的数据格式,通常按状态码分组:
- 成功响应:200/201 状态码下的 Body 结构。
- 错误响应:4xx/5xx 状态码下的统一错误格式。
- 响应头:如
RateLimit-Remaining、X-Request-Id。
# OpenAPI 3.0 响应结构示例
responses:
'200':
description: 订单创建成功
content:
application/json:
schema:
type: object
properties:
orderId:
type: string
status:
type: string
enum: [pending, confirmed, shipped]
createdAt:
type: string
format: date-time
'400':
description: 请求参数错误
content:
application/json:
$ref: '#/components/schemas/ErrorResponse'
2.3.3 状态码(Status Codes)
状态码是契约中最容易被忽视但最关键的部分。每个端点必须明确声明可能返回的状态码及其语义。
| 状态码 | 语义 | 典型场景 |
|---|---|---|
| 200 OK | 请求成功 | GET 获取资源、PUT 更新成功 |
| 201 Created | 资源创建成功 | POST 创建新订单 |
| 204 No Content | 操作成功,无返回体 | DELETE 删除资源 |
| 400 Bad Request | 客户端请求格式错误 | 缺少必填字段、参数类型错误 |
| 401 Unauthorized | 未认证或认证失败 | Token 过期、无效 |
| 403 Forbidden | 无权限访问 | 角色权限不足 |
| 404 Not Found | 资源不存在 | 查询不存在的用户 ID |
| 409 Conflict | 资源冲突 | 重复创建、版本冲突 |
| 422 Unprocessable Entity | 业务校验失败 | 库存不足、金额超限 |
| 500 Internal Server Error | 服务端内部错误 | 数据库异常、未捕获异常 |
常见陷阱:不要将所有错误都返回 400 或 500。明确区分 400(格式错误)、422(业务校验失败)、409(冲突)等状态码,能显著提升客户端错误处理的精准度。
2.3.4 错误格式(Error Format)
统一的错误格式是契约治理中最容易被忽略但最能体现工程成熟度的要素。一个标准化的错误响应应包含:
- code:机器可读的错误码(如
INSUFFICIENT_STOCK)。 - message:人类可读的错误描述。
- details:可选的错误详情列表,用于字段级校验。
- traceId:请求追踪 ID,便于排查问题。
# 统一错误格式示例(JSON)
{
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": [
{
"field": "quantity",
"message": "数量必须大于 0",
"code": "INVALID_VALUE"
},
{
"field": "productId",
"message": "商品 ID 不能为空",
"code": "MISSING_FIELD"
}
],
"traceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
2.4 契约要素关系图
下图展示了 API 契约中请求、响应、状态码与错误格式之间的交互关系:
2.5 契约治理的实践要点
敏捷团队最佳实践:在 Sprint 计划阶段,由后端、前端、QA 三方共同评审 API 契约。契约评审通过后,将其纳入版本控制(如 Git),并作为 Sprint Backlog 的一部分。任何契约变更都需要触发重新评审。
在敏捷团队中推行契约治理,需要关注以下关键点:
- 契约优先:在编写任何实现代码之前,先定义契约文件(如 OpenAPI YAML)。
- 自动化校验:在 CI 流水线中集成契约校验工具(如 Spectral、Vacuum),确保实现与契约一致。
- 版本管理:对契约进行语义化版本管理(SemVer),明确 Major/Minor/Patch 变更规则。
- 消费者驱动:允许消费者提出契约变更需求,通过协商达成一致。