a6-recipe-api-versioning
概览
API 版本管理允许你在不影响现有客户 端的情况下持续演进 API。APISIX 支持通过路由规则、请求头匹配和流量拆分实现多种版本策略,并可使用 a6 CLI 完成配置。
涵盖的策略:
- URI 路径版本管理——
/v1/users、/v2/users - 基于请求头的版本管理——
Accept: application/vnd.api.v2+json - 查询参数版本管理——
?version=2 - 渐进式迁移——在不同版本间按权重分配流量
- 版本弃用——将旧版本重定向到新版本
适用场景
- 为现有 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-split 与 vars,通过同一个 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条目表示使用路由的默认上游。权重9与1对应 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。