跳到主要内容

a6-recipe-api-versioning

概览

API 版本管理允许你在不影响现有客户端的情况下持续演进 API。APISIX 支持通过路由规则、请求头匹配和流量拆分实现多种版本策略,并可使用 a6 CLI 完成配置。

涵盖的策略:

  1. URI 路径版本管理——/v1/users/v2/users
  2. 基于请求头的版本管理——Accept: application/vnd.api.v2+json
  3. 查询参数版本管理——?version=2
  4. 渐进式迁移——在不同版本间按权重分配流量
  5. 版本弃用——将旧版本重定向到新版本

适用场景

  • 为现有 API 引入破坏性变更
  • 同时运行多个API版本
  • 逐步将客户端从v1迁移到v2
  • 使用用户友好的重定向弃用旧API版本

方法 A:URI 路径版本管理

这是最常见的模式。每个版本使用独立的 URI 前缀,proxy-rewrite 在请求转发到后端前移除版本前缀。

1. 创建版本的上游

a6 upstream create -f - <<'EOF'
{
"id": "api-v1",
"type": "roundrobin",
"nodes": { "api-v1-backend:8080": 1 }
}
EOF

a6 upstream create -f - <<'EOF'
{
"id": "api-v2",
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
}
EOF

2. 用 URI 重写创建版本路由

# v1: /v1/users/123 → /users/123 on api-v1 backend
a6 route create -f - <<'EOF'
{
"id": "route-v1",
"uri": "/v1/*",
"upstream_id": "api-v1",
"plugins": {
"proxy-rewrite": {
"regex_uri": ["^/v1/(.*)", "/$1"]
}
}
}
EOF

# v2: /v2/users/123 → /users/123 on api-v2 backend
a6 route create -f - <<'EOF'
{
"id": "route-v2",
"uri": "/v2/*",
"upstream_id": "api-v2",
"plugins": {
"proxy-rewrite": {
"regex_uri": ["^/v2/(.*)", "/$1"]
}
}
}
EOF

客户端调用/v1/users/v2/users,后端始终看到/users

方法 B:基于请求头的版本管理

路由根据 Accept 请求头进行匹配,并结合 traffic-splitvars,通过同一个 URI 提供多个版本。

a6 route create -f - <<'EOF'
{
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{ "vars": [["http_accept", "~~", "application/vnd\\.api\\.v2\\+json"]] }
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
},
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "api-v1-backend:8080": 1 }
}
}
EOF
  • Accept: application/vnd.api.v2+json→ v2后端
  • 任何其他Accept值→v1后端(默认上游)
  • ~~是APISIX vars表达式中的正则表达式匹配运算符

方法 C:查询参数版本管理

根据 ?version=2 查询参数进行路由。

a6 route create -f - <<'EOF'
{
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{ "vars": [["arg_version", "==", "2"]] }
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
},
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "api-v1-backend:8080": 1 }
}
}
EOF
  • /api/users?version=2→ v2后端
  • /api/users/api/users?version=1→ v1后端

渐进版本迁移

使用加权流量拆分,逐步将流量从 v1 迁移到 v2。

初始阶段:90% v1、10% v2

a6 route create -f - <<'EOF'
{
"id": "api-migration",
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
},
"weight": 1
},
{ "weight": 9 }
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": { "api-v1-backend:8080": 1 }
}
}
EOF

调整为 50/50

a6 route update api-migration -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
},
"weight": 1
},
{ "weight": 1 }
]
}
]
}
}
}
EOF

完成迁移:100% v2

a6 route update api-migration -f - <<'EOF'
{
"upstream": {
"type": "roundrobin",
"nodes": { "api-v2-backend:8080": 1 }
},
"plugins": {}
}
EOF

使用重定向弃用旧版本

弃用 v1 时,使用 301 Moved Permanently 将客户端重定向到 v2。

a6 route update route-v1 -f - <<'EOF'
{
"uri": "/v1/*",
"plugins": {
"redirect": {
"regex_uri": ["^/v1/(.*)", "/v2/$1"],
"ret_code": 301
}
}
}
EOF

调用 /v1/users 的客户端会收到:

HTTP/1.1 301 Moved Permanently
Location: /v2/users

声明版本配置

# apisix-versioning.yaml
upstreams:
- id: api-v1
type: roundrobin
nodes:
"api-v1-backend:8080": 1
- id: api-v2
type: roundrobin
nodes:
"api-v2-backend:8080": 1

routes:
- id: route-v1
uri: "/v1/*"
upstream_id: api-v1
plugins:
proxy-rewrite:
regex_uri: ["^/v1/(.*)", "/$1"]
- id: route-v2
uri: "/v2/*"
upstream_id: api-v2
plugins:
proxy-rewrite:
regex_uri: ["^/v2/(.*)", "/$1"]
a6 config diff -f apisix-versioning.yaml
a6 config sync -f apisix-versioning.yaml

注意事项

  • regex_uri 是包含两个字符串的数组——格式为 ["pattern", "replacement"],而不是对象。匹配模式使用与 PCRE 兼容的 Lua 正则表达式。
  • 流量拆分的权重语义——不包含 upstream 字段的 weighted_upstreams 条目表示使用路由的默认上游。权重 91 对应 90% 与 10% 的流量。
  • ~~ 运算符——用于在 vars 表达式中执行正则匹配。在 JSON 中必须对反斜杠进行双重转义,例如 "application/vnd\\\\.api\\\\.v2\\\\+json"
  • 规则顺序很重要——流量拆分规则按从上到下的顺序求值,首个匹配的规则生效。
  • URI 重写发生在转发到上游之前——proxy-rewrite 改变后端看到的 URI,不改变用于路由匹配的 URI。
  • redirect 是终止型插件——启用重定向后,请求不会到达上游。应移除 upstream_id,避免配置产生歧义。

验证

# Test URI path versioning
curl http://localhost:9080/v1/users # → v1 backend
curl http://localhost:9080/v2/users # → v2 backend

# Test header-based versioning
curl -H "Accept: application/vnd.api.v2+json" http://localhost:9080/api/users # → v2
curl http://localhost:9080/api/users # → v1 (default)

# Test query parameter versioning
curl "http://localhost:9080/api/users?version=2" # → v2
curl http://localhost:9080/api/users # → v1

# Test redirect (deprecation)
curl -v http://localhost:9080/v1/users # → 301 to /v2/users

本文根据 api7/a6 仓库中的 a6-recipe-api-versioning/SKILL.md 生成。可在 AI Agent Skills 页面浏览全部 Skill。