versioning 子包为你的 API 提供 semver 版本控制。它实现了书写在 api-guidelines 中所有的建议。

版本比较是通过go-version 包比较的。它支持匹配像 >= 1.0,< 3 这种比较模式。

  1. import (
  2. // [...]
  3. "github.com/kataras/iris/v12"
  4. "github.com/kataras/iris/v12/versioning"
  5. )

特性(Features)

  • 每个路由版本匹配,一个寻常的 Iris 处理器通过version => 处理器的映射来作 switch-case 匹配。
  • 每个组的版本路由和弃用API
  • 版本匹配类似 >=1.0,<2.02.0.1等等形式
  • 版本not found处理器(可以通过简单的添加版本来自定义。NotFound:在映射上自定义 NotMatchVersionHandler)
  • 版本从 AcceptAccept-Version 头部取回(可以通过中间件自定义)
  • 如果版本找到,响应具有 X-API-Version
  • 通过 Deprecated 包装器,弃用了自定义 X-API-WarnX-API-Deprecation-DataX-API-Deprecation-Info 头部的选项

获取版本(Get version)

当前请求的版本通过 versioning.GetVersion(ctx) 获得。 默认情况下 GetVersion 将尝试读取以下内容:

  • Accept header, i.e Accept: "application/json; version=1.0"
  • Accept-Version header, i.e Accept-Version: "1.0"

你也可以在中间件中通过使用 context 存储值来设置一个自定义的版本。例如:

  1. func(ctx iris.Context) {
  2. ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0"))
  3. ctx.Next()
  4. }

将版本与处理程序匹配(Match version to handler)

versioning.NewMatcher(versioning.Map) iris.Handler 创建一个简单的处理器,这个处理器决定基于请求的版本,哪个处理器需要被执行。

  1. app := iris.New()
  2. // middleware for all versions.
  3. myMiddleware := func(ctx iris.Context) {
  4. // [...]
  5. ctx.Next()
  6. }
  7. myCustomNotVersionFound := func(ctx iris.Context) {
  8. ctx.StatusCode(404)
  9. ctx.Writef("%s version not found", versioning.GetVersion(ctx))
  10. }
  11. userAPI := app.Party("/api/user")
  12. userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{
  13. "1.0": sendHandler(v10Response),
  14. ">= 2, < 3": sendHandler(v2Response),
  15. versioning.NotFound: myCustomNotVersionFound,
  16. }))

弃用(Deprecation)

使用 versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler 函数,你可以标记特定的处理器版本为被弃用的。

  1. v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{
  2. // if empty defaults to: "WARNING! You are using a deprecated version of this API."
  3. WarnMessage string
  4. DeprecationDate time.Time
  5. DeprecationInfo string
  6. })
  7. userAPI.Get("/", versioning.NewMatcher(versioning.Map{
  8. "1.0": v10Handler,
  9. // [...]
  10. }))

这将会让处理器发送这些头到客户端:

  • "X-API-Warn": options.WarnMessage
  • "X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))
  • "X-API-Deprecation-Info": options.DeprecationInfo

如果你不在意时间和日期,你可以使用versioning.DefaultDeprecationOptions替代选项。

通过版本分组路由

也可以按版本对路由进行分组。

使用 versioning.NewGroup(version string) *versioning.Group 函数你可以创建一个组来注册你的版本路由。versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group) 必须在最后被调用,为了使用路由被注册到特定的 Party 中。

  1. app := iris.New()
  2. userAPI := app.Party("/api/user")
  3. // [... static serving, middlewares and etc goes here].
  4. userAPIV10 := versioning.NewGroup("1.0")
  5. userAPIV10.Get("/", sendHandler(v10Response))
  6. userAPIV2 := versioning.NewGroup(">= 2, < 3")
  7. userAPIV2.Get("/", sendHandler(v2Response))
  8. userAPIV2.Post("/", sendHandler(v2Response))
  9. userAPIV2.Put("/other", sendHandler(v2Response))
  10. versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2)

使用上面我们学习的方法,一个中间件仅仅只能被注册到实际的 iris.Party,即使用 versioning.Match为了检测当 x 或者没有版本要求的时候你想使用什么 code/handler 来执行。

弃用组(Deprecation for Group)

仅在你想要改变你的API消费者的组上调用 Deprecated(versioning.DeprecationOptions),这个指定的版本就被丢弃了。

  1. userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions)

在内部的处理器中手动比较版本

  1. // reports if the "version" is matching to the "is".
  2. // the "is" can be a constraint like ">= 1, < 3".
  3. If(version string, is string) bool
  1. // same as `If` but expects a Context to read the requested version.
  2. Match(ctx iris.Context, expectedVersion string) bool
  3. app.Get("/api/user", func(ctx iris.Context) {
  4. if versioning.Match(ctx, ">= 2.2.3") {
  5. // [logic for >= 2.2.3 version of your handler goes here]
  6. return
  7. }
  8. })