Files
2026-07-13 21:36:01 +08:00

394 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
# API 版本控制策略
## 为什么要对 API 进行版本控制?
API 版本控制允许你在演进 API 的同时,保持对现有客户端的向后兼容性。破坏性变更需要发布新版本。
### 破坏性变更
需要发布新版本的变更:
- 删除或重命名字段
- 更改字段类型(如字符串改为整数)
- 在请求中新增必填字段
- 更改响应结构
- 删除端点
- 更改同一场景下的 HTTP 状态码
- 更改认证机制
### 非破坏性变更
不需要发布新版本的安全变更:
- 新增端点
- 新增可选请求字段
- 在响应中新增字段(客户端应忽略未知字段)
- 修复 Bug
- 性能优化
- 为已有资源新增 HTTP 方法
## 版本控制策略
### 1. URI 版本控制
最常见且最直观的方式。版本号作为 URL 路径的一部分。
```http
GET /v1/users/123
GET /v2/users/123
```
**优点:**
- 在 URL 中清晰可见
- 易于理解与实现
- 路由和缓存简单
- 可同时运行多个版本
**缺点:**
- 违反 REST 原则(同一资源对应不同 URI)
- 修改版本号需要更新客户端代码
- 可能导致 URI 泛滥
**实现方式:**
```
/v1/users
/v1/products
/v2/users # 包含破坏性变更的新版本
/v2/products
```
### 2. 标头版本控制
版本号通过 HTTP 标头(Accept 标头或自定义标头)指定。
**Accept 标头:**
```http
GET /users/123
Accept: application/vnd.myapi.v1+json
GET /users/123
Accept: application/vnd.myapi.v2+json
```
**自定义标头:**
```http
GET /users/123
API-Version: 1
GET /users/123
API-Version: 2
```
**优点:**
- URI 保持稳定
- 更符合 REST 风格(同一资源对应同一 URI)
- 将版本控制与资源标识分离
**缺点:**
- 不够直观(调试难度更大)
- 路由更复杂
- 在浏览器中测试困难
- 缓存更复杂
### 3. 查询参数版本控制
版本号通过查询参数指定。
```http
GET /users/123?version=1
GET /users/123?version=2
# 或
GET /users/123?api-version=1
GET /users/123?api-version=2
```
**优点:**
- 实现简单
- 易于测试
- 在 URL 中可见
**缺点:**
- 污染查询字符串
- 不够语义化(版本不是筛选条件)
- 可能与其他查询参数冲突
### 4. 内容协商
客户端通过内容协商指定所需的版本。
```http
GET /users/123
Accept: application/vnd.myapi+json; version=1
GET /users/123
Accept: application/vnd.myapi+json; version=2
```
**优点:**
- 非常符合 REST 风格
- 灵活的内容类型协商
- URI 稳定
**缺点:**
- 实现复杂
- 对开发者不够直观
- 测试难度更大
## 推荐方案
**推荐大多数 API 使用 URI 版本控制**,原因如下:
- 最明确、最易于发现
- 易于理解和调试
- 实现和维护简单
- 版本之间界限清晰
```
/v1/users
/v2/users
/v3/users
```
## 版本格式
### 仅使用主版本号
公开 API 使用简单的主版本号(v1、v2、v3):
```
/v1/users
/v2/users
```
**优点:**
- 简单明了
- 易于沟通
- 促使开发者慎重考虑破坏性变更
### 基于日期的版本号
部分 API 使用日期作为版本号:
```
/2024-01-01/users
/2024-06-15/users
```
**使用者:** Stripe、GitHub API
**优点:**
- 版本发布时间一目了然
- 时间线清晰易懂
- 不会混淆主版本号与次版本号
**缺点:**
- 对客户端不够直观
- 难以了解具体变更内容
## 版本生命周期
### 1. 引入阶段
新版本与旧版本同时发布:
```
/v1/users # 仍受支持
/v2/users # 新版本可用
```
发布新版本时需公告:
- 解释变更的博客文章
- 迁移指南
- 破坏性变更列表
- v1 版本的弃用时间表
### 2. 弃用阶段
将旧版本标记为已弃用,但保持其继续运行:
```http
GET /v1/users/123
响应:
Deprecation: true
Sunset: Wed, 15 Jan 2025 00:00:00 GMT
Link: </v2/users/123>; rel="successor-version"
{
"id": 123,
"name": "John Doe"
}
```
**弃用标头:**
- `Deprecation: true` — 表示该版本已弃用
- `Sunset: <date>` — 该版本将于何时移除(RFC 8594)
- `Link: <url>; rel="successor-version"` — 指向新版本
### 3. 退役阶段
旧版本在公布的日期正式下线。
对已弃用的端点返回 410 Gone:
```http
GET /v1/users/123
响应:410 Gone
{
"error": {
"code": "VERSION_SUNSET",
"message": "API v1 已于 2025-01-15 退役。请使用 v2。",
"documentation_url": "https://api.example.com/docs/migration-v1-to-v2"
}
}
```
## 弃用策略
### 推荐时间线
1. **宣布弃用** — 至少在退役前 6 个月
2. **支持期** — 两个版本同时运行 6-12 个月
3. **退役日期** — 提前明确告知具体日期
4. **宽限期** — 在完全关闭前,提供 30 天的 410 Gone 响应
### 沟通渠道
- API 响应标头
- 发送给注册开发者的邮件
- 博客文章和更新日志
- 控制台通知
- 文档更新
- 状态页面公告
## 迁移策略
### 提供迁移指南
```markdown
# 从 v1 迁移至 v2
## 破坏性变更
### 用户资源变更
**v1**
```json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
```
**v2**
```json
{
"id": 123,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
```
**迁移步骤:**
-`name` 字段拆分为 `first_name``last_name`
- 更新客户端代码以使用新字段
```
### 提供辅助工具
- 迁移脚本
- SDK 更新
- API 差异对比工具
- 兼容层(临时)
## 版本发现
### 根端点
```http
GET /
响应:
{
"versions": {
"v1": {
"status": "deprecated",
"sunset_date": "2025-01-15",
"documentation_url": "https://api.example.com/docs/v1"
},
"v2": {
"status": "current",
"documentation_url": "https://api.example.com/docs/v2"
},
"v3": {
"status": "beta",
"documentation_url": "https://api.example.com/docs/v3"
}
}
}
```
### 版本信息端点
```http
GET /v2/version
响应:
{
"version": "v2",
"released": "2024-01-15",
"status": "stable",
"sunset_date": null
}
```
## OpenAPI 版本控制
### 每个版本独立规范文件
```
openapi-v1.yaml
openapi-v2.yaml
openapi-v3.yaml
```
每个规范文件都是完整且独立的。
### 单一规范多服务器
```yaml
openapi: 3.1.0
info:
title: My API
version: 2.0.0
servers:
- url: https://api.example.com/v1
description: 版本 1(已弃用)
- url: https://api.example.com/v2
description: 版本 2(当前版本)
```
## 最佳实践
1. **从第一天起就做版本控制** — 从 /v1 开始,而不是 /api
2. **仅使用主版本号** — 使用 v1、v2、v3(而不是 v1.1、v1.2
3. **提供较长的弃用期** — 给客户端留出迁移时间(6-12 个月)
4. **沟通要清晰** — 使用标头、文档、邮件
5. **维护旧版本** — 至少同时支持两个版本
6. **记录变更** — 提供详细的迁移指南
7. **使用语义化版本控制** — 用于内部/SDK 版本控制
8. **永远不要在没有警告的情况下破坏兼容性** — 始终提前公告破坏性变更
9. **提供辅助工具** — 迁移脚本、更新后的 SDK
10. **监控使用情况** — 追踪哪些版本正在被使用
## 反模式
避免以下错误:
- **破坏性变更但不升级版本** — 破坏现有客户端
- **版本过多** — 维护噩梦(最多保持 2-3 个活跃版本)
- **弃用期过短** — 让开发者感到困扰
- **没有迁移路径** — 让升级变得痛苦
- **突然退役** — 毫无预警地破坏生产应用
- **版本控制策略不一致** — 不同端点使用不同策略
- **对单个端点进行版本控制** — 应在整个 API 中使用一致的版本