接口治理 / 已完成
接口契约、版本管理和文档
理解接口契约、OpenAPI、字段变更、版本管理、错误码和契约测试。
返回文章积累一句话:接口契约就是前后端共同遵守的约定,版本管理和文档是为了让这个约定在项目变大、人员变多之后仍然不乱。
本篇学完你会什么:知道接口文档应该写什么,字段变更怎么不坑前端,为什么要有版本号,以及如何用 OpenAPI、示例和契约测试减少联调成本。
1. 接口契约是什么
接口契约就是前后端之间的合同。
它至少约定:
请求地址
请求方法
请求参数
请求头
响应结构
错误码
权限要求
分页规则
字段含义如果没有契约,联调时就会变成:
前端:你这个字段怎么没返回?
后端:我以为你不用。
前端:错误码为什么变了?
后端:我昨天顺手改了。契约不是为了写文档而写文档,而是为了减少猜。
2. 一份好接口文档要写什么
以用户列表接口为例:
GET /api/v1/users?page=1&page_size=20&keyword=tom
Authorization: Bearer <token>文档应该包含:
| 内容 | 示例 |
|---|---|
| 接口用途 | 查询用户列表 |
| 权限要求 | user:read |
| 请求方法 | GET |
| 请求路径 | /api/v1/users |
| Query 参数 | page、page_size、keyword 的类型、默认值和范围 |
| 响应字段 | items、total、page、page_size 的类型和含义 |
| 错误情况 | 401、403、422 |
| 示例请求 | 带真实参数 |
| 示例响应 | 带完整字段 |
示例响应:
{
"items": [
{
"id": 1,
"username": "tom",
"email": "tom@example.com",
"is_active": true
}
],
"total": 1,
"page": 1,
"page_size": 20
}文档里不要只写字段名,还要写含义。
3. 字段变更为什么容易出问题
接口最怕“悄悄变”。
危险变更:
| 变更 | 为什么危险 |
|---|---|
| 删除字段 | 前端读不到会报错 |
| 字段改名 | 前端旧代码不认识 |
| 类型改变 | number 变 string 会影响计算 |
| 枚举新增 | 前端没有展示逻辑 |
| 错误码改变 | 前端提示和流程失效 |
| 分页结构改变 | 列表页直接坏 |
更安全的变更方式:
先新增字段,不立刻删除旧字段
前端切到新字段
观察一段时间
再移除旧字段比如:
{
"username": "tom",
"display_name": "Tom"
}不要直接把 username 改成 display_name。
4. 接口版本怎么设计
常见版本方式:
/api/v1/users
/api/v2/users什么时候需要新版本?
| 场景 | 是否需要新版本 |
|---|---|
| 新增可选字段 | 通常不需要 |
| 新增接口 | 不需要 |
| 删除字段 | 可能需要 |
| 响应结构大改 | 需要 |
| 认证方式变化 | 需要 |
| 业务语义变化 | 需要 |
不要每次小改都加版本。版本太多会让维护变复杂。
推荐原则:
兼容性新增 -> 留在当前版本
破坏性变化 -> 新版本或分阶段迁移5. OpenAPI 和契约测试怎么用
FastAPI 会自动生成 OpenAPI:
/docs
/openapi.json但自动文档不是全部。你还要把这些写清楚:
from pydantic import BaseModel, Field
class UserListItem(BaseModel):
id: int = Field(description="用户 id")
username: str = Field(description="登录名")
email: str | None = Field(default=None, description="邮箱")
is_active: bool = Field(description="是否启用")契约测试可以简单理解为:
用测试确认接口返回结构没有乱变。示例:
def test_user_list_contract(client, admin_token):
response = client.get(
"/api/v1/users",
headers={"Authorization": f"Bearer {admin_token}"},
)
assert response.status_code == 200
data = response.json()
assert "items" in data
assert "total" in data
assert "page" in data
assert "page_size" in data
assert isinstance(data["items"], list)
assert isinstance(data["total"], int)
assert isinstance(data["page"], int)
assert isinstance(data["page_size"], int)这个测试仍然很简化。真实项目还会检查 items 里每个用户对象的字段,比如 id 是数字、username 是字符串、is_active 是布尔值。它不是替代业务测试,而是守住前后端约定。
6. 前后端协作流程
一个实用流程:
- 后端先写接口草案:路径、字段、错误码。
- 前端看草案,确认页面需要的字段够不够。
- 双方确认后再开发。
- 后端提供 mock 或测试环境接口。
- 前端按契约接入。
- 联调时只修偏差,不临时改约定。
- 接口变更必须同步文档和测试。
字段变更建议写清:
新增字段:什么时候可用
废弃字段:什么时候移除
破坏性变化:影响哪些页面
迁移方式:前端如何兼容7. 常见错误
| 错误 | 后果 | 修正 |
|---|---|---|
| 只靠口头约定 | 联调反复猜 | 写接口契约 |
| 文档没有示例 | 字段含义不清楚 | 加请求和响应示例 |
| 字段悄悄改名 | 前端线上报错 | 先新增后废弃 |
| 错误码不稳定 | 前端提示混乱 | 统一错误码 |
| 每次小改都升版本 | 版本维护复杂 | 只给破坏性变化升版本 |
| 自动文档字段没描述 | 看不懂业务含义 | 给 Pydantic 字段加说明 |
8. 检查清单
[ ] 接口有路径、方法、权限说明
[ ] 请求参数有类型、是否必填、示例
[ ] 响应字段有含义说明
[ ] 错误码和错误消息有约定
[ ] 分页结构统一
[ ] 破坏性变化有迁移计划
[ ] OpenAPI 能正常生成
[ ] 关键接口有契约测试
[ ] 文档和代码变更一起提交9. 小结
| 名词 | 大白话 |
|---|---|
| 接口契约 | 前后端共同遵守的合同 |
| OpenAPI | 接口机器可读说明书 |
| 契约测试 | 防止接口结构偷偷变 |
| 兼容性变更 | 不影响旧前端的新增 |
| 破坏性变更 | 会让旧调用方坏掉的变化 |
| 版本管理 | 管住接口长期演进 |
上一篇建议:先看《数据库备份、迁移和恢复》。
下一篇建议:继续看《生产问题排查 Runbook》,把线上故障处理流程补齐。