mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
c0f48385a8
Former-commit-id: 5bc97f3093655d08e07cfa0a27decda98b6d300f
142 lines
5.1 KiB
Markdown
142 lines
5.1 KiB
Markdown
# Versioning
|
|
|
|
The [versioning](https://github.com/kataras/iris/tree/master/versioning) package provides [semver](https://semver.org/) versioning for your APIs. It implements all the suggestions written at [api-guidelines](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning) and more.
|
|
|
|
|
|
The version comparison is done by the [go-version](https://github.com/hashicorp/go-version) package. It supports matching over patterns like `">= 1.0, < 3"` and etc.
|
|
|
|
## Features
|
|
|
|
- per route version matching, a normal iris handler with "switch" cases via Map for version => handler
|
|
- per group versioned routes and deprecation API
|
|
- version matching like ">= 1.0, < 2.0" or just "2.0.1" and etc.
|
|
- version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map)
|
|
- version is retrieved from the "Accept" and "Accept-Version" headers (can be customized via middleware)
|
|
- respond with "X-API-Version" header, if version found.
|
|
- deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via `Deprecated` wrapper.
|
|
|
|
## Get version
|
|
|
|
Current request version is retrieved by `versioning.GetVersion(ctx)`.
|
|
|
|
By default the `GetVersion` will try to read from:
|
|
- `Accept` header, i.e `Accept: "application/json; version=1.0"`
|
|
- `Accept-Version` header, i.e `Accept-Version: "1.0"`
|
|
|
|
You can also set a custom version for a handler via a middleware by using the context's store values.
|
|
For example:
|
|
```go
|
|
func(ctx iris.Context) {
|
|
ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0"))
|
|
ctx.Next()
|
|
}
|
|
```
|
|
|
|
## Match version to handler
|
|
|
|
The `versioning.NewMatcher(versioning.Map) iris.Handler` creates a single handler which decides what handler need to be executed based on the requested version.
|
|
|
|
```go
|
|
app := iris.New()
|
|
|
|
// middleware for all versions.
|
|
myMiddleware := func(ctx iris.Context) {
|
|
// [...]
|
|
ctx.Next()
|
|
}
|
|
|
|
myCustomNotVersionFound := func(ctx iris.Context) {
|
|
ctx.StatusCode(404)
|
|
ctx.Writef("%s version not found", versioning.GetVersion(ctx))
|
|
}
|
|
|
|
userAPI := app.Party("/api/user")
|
|
userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{
|
|
"1.0": sendHandler(v10Response),
|
|
">= 2, < 3": sendHandler(v2Response),
|
|
versioning.NotFound: myCustomNotVersionFound,
|
|
}))
|
|
```
|
|
|
|
### Deprecation
|
|
|
|
Using the `versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler` function you can mark a specific handler version as deprecated.
|
|
|
|
|
|
```go
|
|
v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{
|
|
// if empty defaults to: "WARNING! You are using a deprecated version of this API."
|
|
WarnMessage string
|
|
DeprecationDate time.Time
|
|
DeprecationInfo string
|
|
})
|
|
|
|
userAPI.Get("/", versioning.NewMatcher(versioning.Map{
|
|
"1.0": v10Handler,
|
|
// [...]
|
|
}))
|
|
```
|
|
|
|
This will make the handler to send these headers to the client:
|
|
|
|
- `"X-API-Warn": options.WarnMessage`
|
|
- `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))`
|
|
- `"X-API-Deprecation-Info": options.DeprecationInfo`
|
|
|
|
> versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info.
|
|
|
|
## Grouping routes by version
|
|
|
|
Grouping routes by version is possible as well.
|
|
|
|
Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes.
|
|
The `versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`.
|
|
|
|
```go
|
|
app := iris.New()
|
|
|
|
userAPI := app.Party("/api/user")
|
|
// [... static serving, middlewares and etc goes here].
|
|
|
|
userAPIV10 := versioning.NewGroup("1.0")
|
|
userAPIV10.Get("/", sendHandler(v10Response))
|
|
|
|
userAPIV2 := versioning.NewGroup(">= 2, < 3")
|
|
userAPIV2.Get("/", sendHandler(v2Response))
|
|
userAPIV2.Post("/", sendHandler(v2Response))
|
|
userAPIV2.Put("/other", sendHandler(v2Response))
|
|
|
|
versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2)
|
|
```
|
|
|
|
> A middleware can be registered to the actual `iris.Party` only, using the methods we learnt above, i.e by using the `versioning.Match` in order to detect what code/handler you want to be executed when "x" or no version is requested.
|
|
|
|
### Deprecation for Group
|
|
|
|
Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated.
|
|
|
|
```go
|
|
userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions)
|
|
```
|
|
|
|
## Compare version manually from inside your handlers
|
|
|
|
```go
|
|
// reports if the "version" is matching to the "is".
|
|
// the "is" can be a constraint like ">= 1, < 3".
|
|
If(version string, is string) bool
|
|
```
|
|
|
|
```go
|
|
// same as `If` but expects a Context to read the requested version.
|
|
Match(ctx iris.Context, expectedVersion string) bool
|
|
```
|
|
|
|
```go
|
|
app.Get("/api/user", func(ctx iris.Context) {
|
|
if versioning.Match(ctx, ">= 2.2.3") {
|
|
// [logic for >= 2.2.3 version of your handler goes here]
|
|
return
|
|
}
|
|
})
|
|
``` |