bigywater.tech
Flask · 105

API 版本控制

API 要升级又不能破坏老客户端,版本怎么管?用 URL 前缀还是请求头?怎么提示调用方老版本要下线?这篇把两种做法都写出来,逐个验证每种请求会得到什么。

2 种
实测的版本控制方式:URL 前缀与请求头
Deprecation / Sunset
只给 v1 响应加的废弃提示头
406
Accept 里没有任何受支持的版本时的状态码

URL 前缀版本

实测 · 本机执行输出
  /v1/users/1    → 200 {"id":1,"name":"Ann Lee"}
  /v2/users/1    → 200 {"email":"ann@x.test","first_name":"Ann","id":1,"last_name":"Lee"}
  /v3/users/1    → 404 (HTML 404 页面)
  url_for 生成的地址: /v1/users/1 /v2/users/1
  路由表:
     ['GET']  /v1/users/<int:i>        → v1.u1
     ['GET']  /v2/users/<int:i>        → v2.u2
     ['GET']  /health                  → health

上一篇:API 分页;下一篇:RESTful URL 设计。

废弃提示头

实测 · 本机执行输出
  /v1/users/1    Deprecation=true Sunset=Wed, 31 Dec 2026 23:59:59 GMT Link=</v2/users>; rel="successor-version"
  /v2/users/1    Deprecation=None Sunset=None Link=None
  /health        Deprecation=None Sunset=None Link=None
  v1 的 404 响应有没有这些头: true

请求头版本

实测 · 本机执行输出
  Accept: …v1+json           → 200 application/vnd.app.v1+json        {"id":1,"name":"Ann Lee"}
  Accept: …v2+json           → 200 application/vnd.app.v2+json        {"email":"ann@x.test","first_name":"Ann","id":1,"last_name":"Lee"}
  Accept: v2 优先 (q 值)        → 200 application/vnd.app.v2+json        {"email":"ann@x.test","first_name":"Ann","id":1,"last_name":"Lee"}
  Accept: */*                → 200 application/vnd.app.v1+json        {"id":1,"name":"Ann Lee"}
  Accept: text/html          → 406 text/html                          
  X-API-Version: 2           → 200 application/vnd.app.v2+json        {"email":"ann@x.test","first_name":"Ann","id":1,"last_name":"Lee"}
  X-API-Version: 9           → 400 text/html                          
  响应里的 Vary 头: None (Flask 不会自动加;共享缓存会把 v1 与 v2 混在一起)

路由代价

实测 · 本机执行输出
  URL 版本:2 个版本 × 1 条规则 = 2 条规则;请求头版本:1 条规则

分步演示

API 版本控制
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:Vary 头缺失在真实 CDN 或代理缓存下的后果、查询参数版本(?version=2)、两个版本共用数据库迁移时的兼容策略、OpenAPI 文档按版本拆分(见第 114 篇)
    京ICP备2026067502号-1京公网安备11010502063487号