进阶 www.iris-go.com 2026-10-20 02:39:14 · 19 阅读
第61章 API 版本控制
API 版本控制(Versioning) versioning 子包为你的 API 提供 semver(语义化版本)支持。它实现了所有最佳实践约定,包括 api-guidelines 文档中的建议。版本号从请求头 "Accept" 和/或 "Accept-Version" 中获取。如果版本匹配成功,服务器会通过自定义响应头 "X-Api-Version" 返回标准化后的版本号。如果没有匹配到版本(缺少版本头或版本未实现),服务器默认返回 501 状态码和纯文本 "version not found"。如果某个资源版本已被服务器标记为弃用,客户端会收到自定义响应头 "X-Api-Warn"、"X-Api-Deprecation-Date" 和 "X-Api-Deprecation-Info",同时请求仍会按预期正常处理。
简介 内部实现上,版本校验使用了无正则、高性能的第三方包 github.com/blang/semver/v4。
合法的版本范围包括:"<1.0.0"、"<="1.0.0"">1.0.0"">=1.0.0""1.0.0"、"=1.0.0"、""1.0.0"(等于)"!1.0.0"、""(不等于)
一个范围(Range)可以由多个子范围用空格分隔组成:
子范围之间可用逻辑 AND 连接: ">1.0.0 <2.0.0" 匹配两个范围之间的版本,如 "1.1.1" 和 "1.8.7",但不匹配 "1.0.0" 或 "2.0.0" ">1.0.0 <3.0.0 !2.0.3-beta.2" 匹配 1.0.0 到 3.0.0 之间的所有版本,但排除 2.0.3-beta.2
子范围也可以用逻辑 OR 连接: "<2.0.0 || >=3.0.0" 匹配 "1.x.x" 和 "3.x.x",但不匹配 "2.x.x"
AND 的优先级高于 OR,且不支持使用括号。
AND 和 OR 可以组合使用: >1.0.0 <2.0.0 || >3.0.0 !4.2.1 匹配 1.2.3、1.9.9、3.1.1,但不匹配 4.2.1 和 2.1.1。
快速开始 导入 versioning 包。
go
import (
// [...]
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/versioning"
)
1
2
3
4
5
6
通过 `versioning.NewGroup(version string) *versioning.Group` 函数,你可以创建一个路由组,用于根据客户端请求的版本注册应用路由。`versioning.Group` 实现了 `iris.Party` 接口。
示例代码:
```golang
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/versioning"
)
func main() {
app := iris.New()
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.WriteString(`Root not found handler.
This will be applied everywhere except the /api/* requests.`)
})
api := app.Party("/api")
// 可选:设置版本别名(字面量字符串)。
// 这里使用 `UseRouter` 而非 `Use`,
// 以便按版本处理 HTTP 错误,但具体取决于你的需求。
api.UseRouter(versioning.Aliases(versioning.AliasMap{
// 如果客户端未提供版本,默认设为 "1.0.0"。
versioning.Empty: "1.0.0",
// 如果客户端提供 "latest" 版本,
// 将比较用的版本设为 "3.0.0"。
"latest": "3.0.0",
}))
// |----------------|
// | 有趣的部分开始 |
// |----------------|
// 基于版本约束创建一个新的 Group,它兼容 Party。
v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0")
// 可选:按版本设置自定义的模板引擎和路径。
v1.RegisterView(iris.HTML("./v1", ".html"))
// 可选:按版本设置自定义的错误处理程序。
// 注意:如果这样做,你还必须
// 为其余的 parties 注册错误处理程序。
v1.OnErrorCode(iris.StatusNotFound, testError("v1"))
// 按版本注册资源。
v1.Get("/", testHandler("v1"))
v1.Get("/render", testView)
// 为了示例完整性,对版本 2 和版本 3 做相同操作。
v2 := versioning.NewGroup(api, ">=2.0.0 <3.0.0")
v2.RegisterView(iris.HTML("./v2", ".html"))
v2.OnErrorCode(iris.StatusNotFound, testError("v2"))
v2.Get("/", testHandler("v2"))
v2.Get("/render", testView)
v3 := versioning.NewGroup(api, ">=3.0.0 <4.0.0")
v3.RegisterView(iris.HTML("./v3", ".html"))
v3.OnErrorCode(iris.StatusNotFound, testError("v3"))
v3.Get("/", testHandler("v3"))
v3.Get("/render", testView)
app.Listen(":8080")
}
func testHandler(v string) iris.Handler {
return func(ctx iris.Context) {
ctx.JSON(iris.Map{
"version": v,
"message": "Hello, world!",
})
}
}
func testError(v string) iris.Handler {
return func(ctx iris.Context) {
ctx.Writef("not found: %s", v)
}
}
func testView(ctx iris.Context) {
ctx.View("index.html")
}
```
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
版本别名 可选,设置版本别名(字面字符串)。goapi.UseRouter(versioning.Aliases(versioning.AliasMap{ // 如果客户端未提供版本,默认为 "1.0.0"。 versioning.Empty: "1.0.0", // 如果客户端提供 "latest" 版本, // 则将与 "3.0.0" 进行比较。 "latest": "3.0.0", }))1
2
3
4
5
6
7
我们使用 UseRouter 而不是 Use 来按版本处理 HTTP 错误,但这由您决定。获取当前版本 版本通过 versioning.GetVersion 函数提取。即使不使用版本控制功能,也可以在任何地方使用它。默认情况下,GetVersion 会尝试从以下位置读取:Accept 请求头,例如 Accept: "application/json; version=1.0.0"Accept-Version 请求头,例如 Accept-Version: "1.0.0"可以通过设置基于请求上下文的版本来自定义:goapi.Use(func(ctx *context.Context) { if version := ctx.URLParam("version"); version != "" { versioning.SetVersion(ctx, version) } ctx.Next() })1
2
3
4
5
6
7
或者使用 FromQuery(version, default) 辅助函数:goapi.Use(versioning.FromQuery("version", "1.0.0"))1
弃用 要标记 API 版本为已弃用,请使用 Deprecated 方法。gov1.Deprecated(versioning.DefaultDeprecationOptions)1
这将使路由处理器向客户端发送一些额外的请求头。"X-API-Warn": options.WarnMessage"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))"X-API-Deprecation-Info": options.DeprecationInfo
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
版本别名 可选,设置版本别名(字面字符串)。goapi.UseRouter(versioning.Aliases(versioning.AliasMap{ // 如果客户端未提供版本,默认为 "1.0.0"。 versioning.Empty: "1.0.0", // 如果客户端提供 "latest" 版本, // 则将与 "3.0.0" 进行比较。 "latest": "3.0.0", }))1
2
3
4
5
6
7
我们使用 UseRouter 而不是 Use 来按版本处理 HTTP 错误,但这由您决定。获取当前版本 版本通过 versioning.GetVersion 函数提取。即使不使用版本控制功能,也可以在任何地方使用它。默认情况下,GetVersion 会尝试从以下位置读取:Accept 请求头,例如 Accept: "application/json; version=1.0.0"Accept-Version 请求头,例如 Accept-Version: "1.0.0"可以通过设置基于请求上下文的版本来自定义:goapi.Use(func(ctx *context.Context) { if version := ctx.URLParam("version"); version != "" { versioning.SetVersion(ctx, version) } ctx.Next() })1
2
3
4
5
6
7
或者使用 FromQuery(version, default) 辅助函数:goapi.Use(versioning.FromQuery("version", "1.0.0"))1
弃用 要标记 API 版本为已弃用,请使用 Deprecated 方法。gov1.Deprecated(versioning.DefaultDeprecationOptions)1
这将使路由处理器向客户端发送一些额外的请求头。"X-API-Warn": options.WarnMessage"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))"X-API-Deprecation-Info": options.DeprecationInfo