Files
xiaoxia-saas/docs/API-VERSIONING.md
Xiaoxia AI c23922faab feat(api): add API versioning and deprecation strategy
- Implement API version management middleware
- Add version lifecycle management (dev/stable/maintenance/deprecated/sunset)
- Add deprecation warning headers (X-API-Deprecated, X-API-Sunset-Date)
- Add version tracking headers (X-API-Version)
- Handle sunset versions with 410 Gone response
- Comprehensive API versioning documentation
- Include migration guide and best practices
- Support gradual version rollout

Phase 4 Task 44/68 completed
2026-06-17 08:41:11 +08:00

5.6 KiB
Raw Permalink Blame History

API 版本管理指南

📋 概述

小虾 SaaS 使用 URL 路径版本管理策略,确保 API 的向后兼容性。


🔢 版本命名规则

当前版本:v1

/api/v1/auth/login
/api/v1/workspaces
/api/v1/projects

版本号规则

  • v1, v2, v3... - 主版本号
  • 破坏性变更才升级主版本
  • 非破坏性变更在当前版本迭代

📝 版本策略

什么时候升级版本?

需要升级(破坏性变更):

  • 修改响应结构
  • 删除字段
  • 修改字段类型
  • 修改认证方式
  • 修改 HTTP 方法

不需要升级(兼容性变更):

  • 添加新字段
  • 添加新端点
  • 添加可选参数
  • 优化性能
  • 修复 Bug

🔄 版本生命周期

阶段 1: 开发中(Development

  • 版本:v2-beta
  • 状态:不稳定,可能变更
  • 使用:仅内部测试

阶段 2: 稳定版(Stable

  • 版本:v2
  • 状态:稳定,推荐使用
  • 支持:完整支持,持续更新

阶段 3: 维护中(Maintenance

  • 版本:v1(当 v2 发布后)
  • 状态:只修复安全问题和严重 Bug
  • 支持:12 个月维护期

阶段 4: 已弃用(Deprecated

  • 版本:v1(维护期结束后)
  • 状态:仍可访问,显示弃用警告
  • 支持:6 个月弃用期

阶段 5: 下线(Sunset

  • 版本:v1(弃用期结束后)
  • 状态:返回 410 Gone
  • 支持:无

📅 版本时间线示例

2026-06-01: v1 发布 (Stable)
2027-01-01: v2 发布 (Stable)
2027-01-01: v1 进入维护期 (Maintenance, 12个月)
2028-01-01: v1 进入弃用期 (Deprecated, 6个月)
2028-07-01: v1 下线 (Sunset)

🚨 弃用通知

响应头

HTTP/1.1 200 OK
X-API-Version: v1
X-API-Deprecated: true
X-API-Sunset-Date: 2028-07-01
X-API-Deprecation-Info: https://docs.xiaoxia-saas.com/api/deprecation/v1

响应体(可选)

{
  "data": { ... },
  "_meta": {
    "deprecated": true,
    "sunset_date": "2028-07-01",
    "migration_guide": "https://docs.xiaoxia-saas.com/api/migration/v1-to-v2"
  }
}

🔧 客户端处理

检测弃用

import requests

response = requests.get("https://api.xiaoxia-saas.com/api/v1/workspaces")

if response.headers.get("X-API-Deprecated") == "true":
    sunset_date = response.headers.get("X-API-Sunset-Date")
    print(f"警告: API v1 将在 {sunset_date} 下线")

自动迁移

class APIClient:
    def __init__(self, version="v2"):
        self.version = version
        self.base_url = f"https://api.xiaoxia-saas.com/api/{version}"
    
    def fallback_to_v1(self, endpoint):
        """自动降级到 v1"""
        try:
            return self.request(endpoint)
        except APINotFoundError:
            # v2 不存在,尝试 v1
            self.version = "v1"
            return self.request(endpoint)

📚 迁移指南

v1 → v2 迁移示例

变更 1: 登录响应结构

v1:

{
  "token": "abc123",
  "user_id": "user-123"
}

v2:

{
  "access_token": "abc123",
  "refresh_token": "def456",
  "expires_in": 1800,
  "user": {
    "id": "user-123",
    "email": "user@example.com"
  }
}

迁移代码:

# v1
token = response.json()["token"]

# v2
token = response.json()["access_token"]

🛠️ 实现方式

FastAPI 版本路由

from fastapi import APIRouter

# v1 路由
api_v1 = APIRouter(prefix="/api/v1")

@api_v1.get("/workspaces")
async def list_workspaces_v1():
    return {"workspaces": []}

# v2 路由
api_v2 = APIRouter(prefix="/api/v2")

@api_v2.get("/workspaces")
async def list_workspaces_v2():
    return {
        "data": [],
        "pagination": {"page": 1, "total": 0}
    }

# 注册到主应用
app.include_router(api_v1)
app.include_router(api_v2)

弃用中间件

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

class DeprecationMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        response = await call_next(request)
        
        if request.url.path.startswith("/api/v1"):
            response.headers["X-API-Deprecated"] = "true"
            response.headers["X-API-Sunset-Date"] = "2028-07-01"
        
        return response

📊 版本使用统计

监控指标

from prometheus_client import Counter

api_version_counter = Counter(
    'api_requests_by_version',
    'API requests by version',
    ['version']
)

@app.middleware("http")
async def track_version(request: Request, call_next):
    version = extract_version(request.url.path)
    api_version_counter.labels(version=version).inc()
    return await call_next(request)

查看统计

# v1 使用量
curl http://localhost:8000/metrics | grep api_requests_by_version{version="v1"}

# v2 使用量
curl http://localhost:8000/metrics | grep api_requests_by_version{version="v2"}

🎯 最佳实践

1. 提前通知

  • 至少提前 6 个月通知弃用
  • 通过邮件、公告、响应头多渠道通知
  • 提供详细的迁移指南

2. 保持兼容

  • 尽可能添加而不是修改
  • 使用可选参数而不是必填
  • 保留旧字段,添加新字段

3. 渐进式迁移

v1 (100%) → v1 (80%) + v2 (20%) → v1 (20%) + v2 (80%) → v2 (100%)

4. 文档优先

  • 版本变更先更新文档
  • 提供完整的迁移指南
  • 包含代码示例

🔗 相关资源


最后更新: 2026-06-17