title: "API 版本管理:策略与最佳实践"
date: "2026-07-10"
tags: ["API设计", "版本管理", "向后兼容", "REST"]
API 版本管理:策略与最佳实践
API 版本管理是长期维护的核心挑战。错误的版本策略会导致客户端崩溃、用户流失。
版本策略对比
| 策略 | 示例 | 优点 | 缺点 |
|------|------|------|------|
| URL 路径 | /v1/users | 直观、易缓存 | URL 变化大 |
| 查询参数 | /users?version=1 | 灵活 | 缓存困难 |
| Header | Accept: application/vnd.api.v1+json | URL 干净 | 调试困难 |
| 子域名 | v1.api.example.com | 可独立部署 | 基础设施复杂 |
URL 路径版本(推荐)
PYTHON
from fastapi import FastAPI, APIRouter
app = FastAPI()
# v1 路由
v1_router = APIRouter(prefix="/v1")
@v1_router.get("/users")
async def get_users_v1():
return {"users": [{"id": 1, "name": "Alice"}]}
@v1_router.get("/users/{user_id}")
async def get_user_v1(user_id: int):
return {"id": user_id, "name": "Alice", "email": "alice@example.com"}
# v2 路由(有破坏性变更)
v2_router = APIRouter(prefix="/v2")
@v2_router.get("/users")
async def get_users_v2(page: int = 1, size: int = 20):
# v2 返回分页结构
return {
"data": [{"id": 1, "name": "Alice", "email": "alice@example.com"}],
"pagination": {"page": page, "size": size, "total": 100}
}
@v2_router.get("/users/{user_id}")
async def get_user_v2(user_id: int, include: str = None):
# v2 支持字段选择
user = {"id": user_id, "name": "Alice", "email": "alice@example.com"}
if include:
fields = include.split(",")
user = {k: v for k, v in user.items() if k in fields}
return user
app.include_router(v1_router)
app.include_router(v2_router)版本协商中间件
PYTHON
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
class APIVersionMiddleware(BaseHTTPMiddleware):
def __init__(self, app, default_version: str = "v1"):
super().__init__(app)
self.default_version = default_version
self.supported_versions = ["v1", "v2", "v3"]
async def dispatch(self, request: Request, call_next):
# 1. 检查 URL 路径版本
path = request.url.path
for version in self.supported_versions:
if path.startswith(f"/{version}/"):
request.state.api_version = version
return await call_next(request)
# 2. 检查 Header 版本
header_version = request.headers.get("X-API-Version")
if header_version and header_version in self.supported_versions:
request.state.api_version = header_version
# 重写路径
new_path = f"/{header_version}{path}"
request.scope["path"] = new_path
return await call_next(request)
# 3. 使用默认版本
request.state.api_version = self.default_version
request.scope["path"] = f"/{self.default_version}{path}"
response = await call_next(request)
response.headers["X-API-Version"] = request.state.api_version
return response向后兼容策略
PYTHON
class BackwardCompatibleAPI:
"""向后兼容的 API 设计"""
def __init__(self):
self.deprecated_fields = {
"v1": ["user_name"], # v1 使用 user_name
"v2": ["username"] # v2 改为 username
}
def transform_response(self, data: dict, version: str) -> dict:
"""根据版本转换响应"""
if version == "v1":
# v1 客户端期望 user_name
if "username" in data:
data["user_name"] = data.pop("username")
# v1 不支持嵌套对象
if "profile" in data and isinstance(data["profile"], dict):
data.update(data.pop("profile"))
elif version == "v2":
# v2 使用 username
if "user_name" in data:
data["username"] = data.pop("user_name")
return data
def transform_request(self, data: dict, version: str) -> dict:
"""根据版本转换请求"""
if version == "v1":
# v1 客户端发送 user_name
if "user_name" in data:
data["username"] = data.pop("user_name")
return data废弃通知
PYTHON
from datetime import datetime, timedelta
class DeprecationManager:
def __init__(self):
self.deprecations = {}
def mark_deprecated(self, version: str, sunset_date: datetime, migration_guide: str):
self.deprecations[version] = {
"sunset_date": sunset_date,
"migration_guide": migration_guide,
"deprecated_at": datetime.utcnow()
}
def get_headers(self, version: str) -> dict:
"""返回废弃相关的 HTTP 头"""
headers = {}
if version in self.deprecations:
dep = self.deprecations[version]
headers["Deprecation"] = dep["deprecated_at"].strftime("%a, %d %b %Y %H:%M:%S GMT")
headers["Sunset"] = dep["sunset_date"].strftime("%a, %d %b %Y %H:%M:%S GMT")
headers["Link"] = f'<{dep["migration_guide"]}>; rel="successor-version"'
return headers
# 使用
deprecation_mgr = DeprecationManager()
deprecation_mgr.mark_deprecated(
version="v1",
sunset_date=datetime.utcnow() + timedelta(days=180),
migration_guide="https://docs.example.com/migration/v1-to-v2"
)
@app.middleware("http")
async def add_deprecation_headers(request: Request, call_next):
response = await call_next(request)
version = getattr(request.state, "api_version", "v1")
headers = deprecation_mgr.get_headers(version)
for key, value in headers.items():
response.headers[key] = value
return response版本生命周期
CODE
v1 发布 → v2 开发 → v2 发布 → v1 标记废弃 → v1 日落 → v1 下线
↓ ↓ ↓ ↓ ↓ ↓
活跃 活跃 活跃 警告头 只读 404最佳实践
| 实践 | 说明 |
|------|------|
| 最多支持 2-3 个版本 | 减少维护负担 |
| 提前 6 个月通知废弃 | 给客户端迁移时间 |
| 提供迁移指南 | 降低迁移成本 |
| 监控版本使用率 | 数据驱动下线决策 |
| 语义化版本 | 主版本号表示破坏性变更 |
API 版本管理是长期承诺。选择正确的策略,能让 API 在演进的同时保持稳定。
读者评论 2