API 版本管理 #
为什么需要版本管理? #
API 版本管理是 API 设计中至关重要的一环,它确保 API 在演进过程中保持向后兼容,同时允许引入新功能和改进。
text
┌─────────────────────────────────────────────────────────────┐
│ 版本管理的必要性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题场景: │
│ │
│ 1. API 需要变更 │
│ - 添加新功能 │
│ - 修改数据结构 │
│ - 废弃旧接口 │
│ │
│ 2. 客户端兼容性 │
│ - 旧客户端仍在使用 │
│ - 不能强制所有客户端同时升级 │
│ - 需要过渡期 │
│ │
│ 3. 风险控制 │
│ - 新版本可能有 bug │
│ - 需要灰度发布 │
│ - 需要回滚能力 │
│ │
│ 解决方案:版本管理 │
│ ✅ 多版本共存 │
│ ✅ 平滑过渡 │
│ ✅ 风险可控 │
│ │
└─────────────────────────────────────────────────────────────┘
版本号规范 #
语义化版本 #
text
┌─────────────────────────────────────────────────────────────┐
│ 语义化版本号 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 格式:MAJOR.MINOR.PATCH │
│ │
│ 示例:2.1.3 │
│ │ │ │ │
│ │ │ └── PATCH:修复 bug,向后兼容 │
│ │ └─── MINOR:新增功能,向后兼容 │
│ └───── MAJOR:重大变更,不兼容 │
│ │
│ API 版本简化: │
│ 通常只使用 MAJOR 版本号 │
│ /v1/users │
│ /v2/users │
│ │
│ 原因: │
│ - MINOR 和 PATCH 版本向后兼容 │
│ - 客户端无需感知小版本变更 │
│ - 简化版本管理 │
│ │
└─────────────────────────────────────────────────────────────┘
版本变更规则 #
text
┌─────────────────────────────────────────────────────────────┐
│ 版本变更规则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 需要升级主版本号(Breaking Changes): │
│ ───────────────────────────────────────────── │
│ - 删除端点 │
│ - 重命名端点 │
│ - 修改请求/响应结构 │
│ - 修改必填字段 │
│ - 修改认证方式 │
│ │
│ 不需要升级主版本号(Non-Breaking Changes): │
│ ───────────────────────────────────────────── │
│ - 新增端点 │
│ - 新增可选字段 │
│ - 新增查询参数 │
│ - 修复 bug │
│ - 性能优化 │
│ │
└─────────────────────────────────────────────────────────────┘
版本管理策略 #
策略一:URL 路径版本(推荐) #
text
┌─────────────────────────────────────────────────────────────┐
│ URL 路径版本 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 格式:/v{version}/resource │
│ │
│ 示例: │
│ GET /v1/users │
│ GET /v2/users │
│ GET /v1/users/123 │
│ GET /v2/users/123 │
│ │
│ 优点: │
│ ✅ 简单直观,易于理解 │
│ ✅ 易于缓存(URL 不同) │
│ ✅ 易于测试和调试 │
│ ✅ 浏览器可直接访问 │
│ │
│ 缺点: │
│ ❌ URL 变化 │
│ ❌ 需要维护多个路由 │
│ │
│ 实现示例: │
│ // Express.js │
│ app.use('/v1', v1Router); │
│ app.use('/v2', v2Router); │
│ │
│ // Nginx │
│ location /v1/ { │
│ proxy_pass http://api-v1/; │
│ } │
│ location /v2/ { │
│ proxy_pass http://api-v2/; │
│ } │
│ │
└─────────────────────────────────────────────────────────────┘
策略二:Header 版本 #
text
┌─────────────────────────────────────────────────────────────┐
│ Header 版本 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方式一:自定义 Header │
│ GET /users HTTP/1.1 │
│ Host: api.example.com │
│ X-API-Version: 2 │
│ │
│ 方式二:Accept Header │
│ GET /users HTTP/1.1 │
│ Host: api.example.com │
│ Accept: application/vnd.myapi.v2+json │
│ │
│ 优点: │
│ ✅ URL 不变 │
│ ✅ 更符合 REST 原则 │
│ ✅ 支持内容协商 │
│ │
│ 缺点: │
│ ❌ 不够直观 │
│ ❌ 浏览器直接访问困难 │
│ ❌ 缓存配置复杂 │
│ │
│ 实现示例: │
│ // Express.js 中间件 │
│ app.use((req, res, next) => { │
│ const version = req.headers['x-api-version'] || '1'; │
│ req.version = version; │
│ next(); │
│ }); │
│ │
└─────────────────────────────────────────────────────────────┘
策略三:查询参数版本 #
text
┌─────────────────────────────────────────────────────────────┐
│ 查询参数版本 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 格式:/resource?version={version} │
│ │
│ 示例: │
│ GET /users?version=1 │
│ GET /users?version=2 │
│ GET /users/123?version=1 │
│ │
│ 优点: │
│ ✅ URL 路径不变 │
│ ✅ 简单易用 │
│ ✅ 可选参数,默认使用最新版本 │
│ │
│ 缺点: │
│ ❌ 参数可能被忽略 │
│ ❌ 与其他查询参数混淆 │
│ ❌ 缓存策略复杂 │
│ │
│ 实现示例: │
│ // Express.js │
│ app.get('/users', (req, res) => { │
│ const version = req.query.version || '1'; │
│ // 根据版本处理请求 │
│ }); │
│ │
└─────────────────────────────────────────────────────────────┘
策略四:子域名版本 #
text
┌─────────────────────────────────────────────────────────────┐
│ 子域名版本 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 格式:v{version}.api.example.com/resource │
│ │
│ 示例: │
│ GET https://v1.api.example.com/users │
│ GET https://v2.api.example.com/users │
│ │
│ 优点: │
│ ✅ URL 路径不变 │
│ ✅ 可以独立部署 │
│ ✅ 易于负载均衡 │
│ │
│ 缺点: │
│ ❌ DNS 配置复杂 │
│ ❌ SSL 证书管理复杂 │
│ ❌ 跨域问题 │
│ │
└─────────────────────────────────────────────────────────────┘
策略对比 #
text
┌─────────────────────────────────────────────────────────────┐
│ 版本策略对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 策略 直观性 缓存性 RESTful 浏览器 推荐度 │
│ ───────────────────────────────────────────────────────── │
│ URL 路径 ⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐⭐ │
│ Header ⭐ ⭐⭐ ⭐⭐⭐ ⭐ ⭐⭐ │
│ 查询参数 ⭐⭐⭐ ⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐ │
│ 子域名 ⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐ │
│ │
│ 推荐: │
│ - 公开 API:URL 路径版本 │
│ - 内部 API:Header 版本 │
│ - 简单场景:查询参数版本 │
│ │
└─────────────────────────────────────────────────────────────┘
版本生命周期管理 #
版本生命周期 #
text
┌─────────────────────────────────────────────────────────────┐
│ 版本生命周期 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 开发 │──►│ 发布 │──►│ 维护 │──►│ 废弃 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 开发阶段: │
│ - 开发新版本功能 │
│ - 内部测试 │
│ - 文档编写 │
│ │
│ 发布阶段: │
│ - 灰度发布 │
│ - 监控反馈 │
│ - 全量发布 │
│ │
│ 维护阶段: │
│ - Bug 修复 │
│ - 安全更新 │
│ - 小功能优化 │
│ │
│ 废弃阶段: │
│ - 发布废弃公告 │
│ - 提供迁移指南 │
│ - 设置过期时间 │
│ - 最终下线 │
│ │
└─────────────────────────────────────────────────────────────┘
废弃 API 处理 #
text
┌─────────────────────────────────────────────────────────────┐
│ 废弃 API 处理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 添加废弃警告 Header │
│ ───────────────────────────────────────────── │
│ HTTP/1.1 200 OK │
│ Deprecation: true │
│ Sunset: Sat, 31 Dec 2025 23:59:59 GMT │
│ Link: </v2/users>; rel="successor-version" │
│ │
│ 2. 响应体中添加警告信息 │
│ ───────────────────────────────────────────── │
│ { │
│ "data": [...], │
│ "warning": { │
│ "code": "DEPRECATED_API", │
│ "message": "This API version is deprecated", │
│ "sunset": "2025-12-31", │
│ "migrationGuide": "https://docs.example.com/migration" │
│ } │
│ } │
│ │
│ 3. 提供迁移时间表 │
│ ───────────────────────────────────────────── │
│ - 发布日期:2025-01-01 │
│ - 废弃公告:2025-06-01 │
│ - 过期日期:2025-12-31 │
│ - 下线日期:2026-01-01 │
│ │
│ 4. 过期后返回 410 Gone │
│ ───────────────────────────────────────────── │
│ HTTP/1.1 410 Gone │
│ │
│ { │
│ "error": { │
│ "code": "API_VERSION_RETIRED", │
│ "message": "API v1 has been retired", │
│ "migrationGuide": "https://docs.example.com/migration" │
│ } │
│ } │
│ │
└─────────────────────────────────────────────────────────────┘
版本管理最佳实践 #
text
┌─────────────────────────────────────────────────────────────┐
│ 版本管理最佳实践 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 向后兼容优先 │
│ - 尽量避免 Breaking Changes │
│ - 新增功能使用可选字段 │
│ - 保留旧字段一段时间 │
│ │
│ 2. 提前通知 │
│ - 提前 6 个月发布废弃公告 │
│ - 提供详细的迁移指南 │
│ - 提供示例代码 │
│ │
│ 3. 渐进式迁移 │
│ - 支持多版本共存 │
│ - 提供迁移工具 │
│ - 监控旧版本使用情况 │
│ │
│ 4. 文档更新 │
│ - 每个版本独立文档 │
│ - 明确标注版本差异 │
│ - 提供变更日志 │
│ │
│ 5. 版本策略一致 │
│ - 整个 API 使用统一的版本策略 │
│ - 不要混用多种版本策略 │
│ │
└─────────────────────────────────────────────────────────────┘
版本管理检查清单 #
text
□ 版本策略
□ 选择合适的版本策略
□ 团队统一使用
□ 文档说明清楚
□ 版本发布
□ 遵循语义化版本规范
□ 提供变更日志
□ 更新 API 文档
□ 版本废弃
□ 提前发布废弃公告
□ 提供迁移指南
□ 设置合理的过渡期
□ 监控旧版本使用情况
□ 版本下线
□ 确认所有客户端已迁移
□ 返回 410 Gone
□ 保留文档供参考
下一步 #
现在你已经了解了 API 版本管理,接下来学习 API 安全,深入了解如何保护 API 安全!
最后更新:2026-03-29