Publish version 11.1.0. Read HISTORY.md for details

Versioning API - https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110

Former-commit-id: 59296266798a8fca3af65497ea63f8f84d60de79
This commit is contained in:
Gerasimos (Makis) Maropoulos 2018-11-18 03:28:23 +02:00 committed by GitHub
commit a638e9ef81
32 changed files with 1170 additions and 41 deletions

6
Gopkg.lock generated
View File

@ -289,6 +289,12 @@
packages = ["internal","redis"]
revision = "2cd21d9966bf7ff9ae091419744f0b3fb0fecace"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-version"
packages = ["."]
revision = "b5a281d3160aa11950a6182bd9a9dc2cb1e02d50"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1

View File

@ -89,3 +89,7 @@
[[constraint]]
branch = "master"
name = "github.com/gomodule/redigo"
[[constraint]]
name = "github.com/hashicorp/go-version"
version = "1.0.0"

View File

@ -17,6 +17,155 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
# Su, 18 November 2018 | v11.1.0
PR: https://github.com/kataras/iris/pull/1130
This release contains a new feature for versioning your Iris APIs. The initial motivation and feature request came by https://github.com/kataras/iris/issues/1129.
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
}
})
```
Example can be found [here](_examples/versioning/main.go).
# Fr, 09 November 2018 | v11.0.4
Add `Configuration.DisablePathCorrectionRedirection` - `iris.WithoutPathCorrectionRedirection` to support
@ -119,7 +268,7 @@ For the craziest of us, click [here](https://github.com/kataras/iris/compare/v10
## Routing
I wrote a [new router implementation](https://github.com/kataras/muxie#philosophy) for our Iris internal(low-level) routing mechanism, it is good to know that this was the second time we have updated the router internals without a single breaking change after the v6, thanks to the very well-writen and designed-first code we have for the high-level path syntax component called [macro interpreter](macro/interpreter).
I wrote a [new router implementation](https://github.com/kataras/muxie#philosophy) for our Iris internal(low-level) routing mechanism, it is good to know that this was the second time we have updated the router internals without a single breaking change after the v6, thanks to the very well-written and designed-first code we have for the high-level path syntax component called [macro interpreter](macro/interpreter).
The new router supports things like **closest wildcard resolution**.

View File

@ -17,6 +17,10 @@
**Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας.
# Su, 18 November 2018 | v11.1.0
Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) για να διαβάσετε στα αγγλικά για το νέο "versioning" feature.
# Fr, 09 November 2018 | v11.0.4
Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) για να διαβάσετε στα αγγλικά τις αλλαγές που φέρνει το τελευταίο patch για την έκδοση 11.

View File

@ -17,6 +17,10 @@ Developers tidak diwajibkan untuk melakukan upgrade apabila mereka tidak membutu
**Cara Upgrade**: Bukan command-line anda dan eksekuis perintah ini: `go get -u github.com/kataras/iris` atau biarkan updater otomatis melakukannya untuk anda.
# Su, 18 November 2018 | v11.1.0
This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) instead.
# Fr, 09 November 2018 | v11.0.4
This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) instead.

View File

@ -17,6 +17,10 @@
**如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。
# Su, 18 November 2018 | v11.1.0
This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) instead.
# Fr, 09 November 2018 | v11.0.4
This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) instead.

View File

@ -1,8 +1,12 @@
# ⚡️ Update: community-driven version 11.1.0
Click [here](HISTORY.md#su-18-november-2018--v1110) to read about the versioning API that the most recent version of Iris brings to you.
# Iris Web Framework <a href="README_ZH.md"> <img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg?v=10" /></a> <a href="README_ID.md"> <img width="20px" src="https://iris-go.com/images/flag-indonesia.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_PT_BR.md"><img width="20px" src="https://iris-go.com/images/flag-pt-br.svg?v=10" /></a> <a href="README_JPN.md"><img width="20px" src="https://iris-go.com/images/flag-japan.svg?v=10" /></a>
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris is a fast, simple yet fully featured and very efficient web framework for Go.
@ -1013,7 +1017,7 @@ Iris, unlike others, is 100% compatible with the standards and that's why the ma
## Support
- [HISTORY](HISTORY.md#fr-09-november-2018--v1104) file is your best friend, it contains information about the latest features and changes
- [HISTORY](HISTORY.md#su-18-november-2018--v1110) file is your best friend, it contains information about the latest features and changes
- Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues)
- Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com)
- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go.
@ -108,7 +108,7 @@ _Η τελευταία ενημέρωση έγινε την [Τρίτη, 21 Νο
## Υποστήριξη
- To [HISTORY](HISTORY_GR.md#fr-09-november-2018--v1104) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές
- To [HISTORY](HISTORY_GR.md#su-18-november-2018--v1110) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές
- Μήπως τυχαίνει να βρήκατε κάποιο bug; Δημοσιεύστε το στα [github issues](https://github.com/kataras/iris/issues)
- Έχετε οποιεσδήποτε ερωτήσεις ή πρέπει να μιλήσετε με κάποιον έμπειρο για την επίλυση ενός προβλήματος σε πραγματικό χρόνο; Ελάτε μαζί μας στην [συνομιλία κοινότητας](https://chat.iris-go.com)
- Συμπληρώστε την αναφορά εμπειρίας χρήστη κάνοντας κλικ [εδώ](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris adalah web framework yang cepat, sederhana namun berfitur lengkap dan sangat efisien untuk Go.
@ -106,7 +106,7 @@ _Diperbarui pada: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_
## Dukungan
- File [HISTORY](HISTORY_ID.md#fr-09-november-2018--v1104) adalah sahabat anda, file tersebut memiliki informasi terkait fitur dan perubahan terbaru
- File [HISTORY](HISTORY_ID.md#su-18-november-2018--v1110) adalah sahabat anda, file tersebut memiliki informasi terkait fitur dan perubahan terbaru
- Apakah anda menemukan bug? Laporkan itu melalui [github issues](https://github.com/kataras/iris/issues)
- Apakah anda memiliki pertanyaan atau butuh untuk bicara kepada seseorang yang sudah berpengalaman untuk menyelesaikan masalah secara langsung? Gabung bersama kami di [community chat](https://chat.iris-go.com)
- Lengkapi laporan user-experience berbasis formulir kami dengan tekan [disini](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Irisはシンプルで高速、それにも関わらず充実した機能を有する効率的なGo言語のウェブフレームワークです。
@ -106,7 +106,7 @@ _Updated at: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_
## 支援
- [HISTORY](HISTORY.md#fr-09-november-2018--v1104)ファイルはあなたの友人です。このファイルには、機能に関する最新の情報や変更点が記載されています。
- [HISTORY](HISTORY.md#su-18-november-2018--v1110)ファイルはあなたの友人です。このファイルには、機能に関する最新の情報や変更点が記載されています。
- バグを発見しましたか?[github issues](https://github.com/kataras/iris/issues)に投稿をお願い致します。
- 質問がありますか?または問題を即時に解決するため、熟練者に相談する必要がありますか?[community chat](https://chat.iris-go.com)に参加しましょう。
- [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)をクリックしてユーザーとしての体験を報告しましょう。

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris é um framework rápido, simples porém completo e muito eficiente para a linguagem Go.
@ -106,7 +106,7 @@ _Atualizado em : [Terça, 21 de Novembro de 2017](_benchmarks/README_UNIX.md)_
## Apoie
- [HISTORY](HISTORY.md#fr-09-november-2018--v1104) o arquivo HISTORY é o seu melhor amigo, ele contém informações sobre as últimas features e mudanças.
- [HISTORY](HISTORY.md#su-18-november-2018--v1110) o arquivo HISTORY é o seu melhor amigo, ele contém informações sobre as últimas features e mudanças.
- Econtrou algum bug ? Poste-o nas [issues](https://github.com/kataras/iris/issues)
- Possui alguma dúvida ou gostaria de falar com alguém experiente para resolver seu problema em tempo real ? Junte-se ao [chat da nossa comunidade](https://chat.iris-go.com).
- Complete nosso formulário de experiência do usuário clicando [aqui](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris - это быстрая, простая, но полнофункциональная и очень эффективная веб-платформа для Go.
@ -106,7 +106,7 @@ _Обновлено: [Вторник, 21 ноября 2017 г.](_benchmarks/READ
## Поддержка
- Файл [HISTORY](HISTORY.md#fr-09-november-2018--v1104) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях
- Файл [HISTORY](HISTORY.md#su-18-november-2018--v1110) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях
- Вы случайно обнаружили ошибку? Опубликуйте ее на [Github вопросы](https://github.com/kataras/iris/issues)
- У Вас есть какие-либо вопросы или Вам нужно поговорить с кем-то, кто бы смог решить Вашу проблему в режиме реального времени? Присоединяйтесь к нам в [чате сообщества](https://chat.iris-go.com)
- Заполните наш отчет о пользовательском опыте на основе формы, нажав [здесь](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris 是一款超快、简洁高效的 Go 语言 Web开发框架。
@ -102,7 +102,7 @@ _更新于: [2017年11月21日星期二](_benchmarks/README_UNIX.md)_
## 支持
- [更新记录](HISTORY_ZH.md#fr-09-november-2018--v1104) 是您最好的朋友,它包含有关最新功能和更改的信息
- [更新记录](HISTORY_ZH.md#su-18-november-2018--v1110) 是您最好的朋友,它包含有关最新功能和更改的信息
- 你碰巧找到了一个错误? 请提交 [github issues](https://github.com/kataras/iris/issues)
- 您是否有任何疑问或需要与有经验的人士交谈以实时解决问题? [加入我们的聊天](https://chat.iris-go.com)
- [点击这里完成我们基于表单的用户体验报告](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -1 +1 @@
11.0.4:https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104
11.1.0:https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110

View File

@ -158,6 +158,11 @@ Navigate through examples for a better understanding.
* [per-route](routing/writing-a-middleware/per-route/main.go)
* [globally](routing/writing-a-middleware/globally/main.go)
### Versioning
- [How it works](https://github.com/kataras/iris/blob/master/versioning/README.md)
- [Example](versioning/main.go)
### hero
- [Basic](hero/basic/main.go)

View File

@ -32,26 +32,61 @@ func main() {
// | {param:string} |
// +------------------------+
// string type
// anything
// anything (single path segmnent)
//
// +-------------------------------+
// | {param:int} or {param:int} |
// | {param:int} |
// +-------------------------------+
// int type
// both positive and negative numbers, any number of digits (ctx.Params().GetInt will limit the digits based on the host arch)
// -9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch
//
// +-------------------------------+
// | {param:int64} or {param:long} |
// +-------------------------------+
// +------------------------+
// | {param:int8} |
// +------------------------+
// int8 type
// -128 to 127
//
// +------------------------+
// | {param:int16} |
// +------------------------+
// int16 type
// -32768 to 32767
//
// +------------------------+
// | {param:int32} |
// +------------------------+
// int32 type
// -2147483648 to 2147483647
//
// +------------------------+
// | {param:int64} |
// +------------------------+
// int64 type
// -9223372036854775808 to 9223372036854775807
//
// +------------------------+
// | {param:uint} |
// +------------------------+
// uint type
// 0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32)
//
// +------------------------+
// | {param:uint8} |
// +------------------------+
// uint8 type
// 0 to 255
//
// +------------------------+
// | {param:uint16} |
// +------------------------+
// uint16 type
// 0 to 65535
//
// +------------------------+
// | {param:uint32} |
// +------------------------+
// uint32 type
// 0 to 4294967295
//
// +------------------------+
// | {param:uint64} |
@ -87,8 +122,8 @@ func main() {
// | {param:path} |
// +------------------------+
// path type
// anything, should be the last part, more than one path segment,
// i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3"
// anything, should be the last part, can be more than one path segment,
// i.e: "/test/{param:path}" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3"
//
// if type is missing then parameter's type is defaulted to string, so
// {param} == {param:string}.

View File

@ -0,0 +1 @@
Head over to the [kataras/iris/versioning/README.md](https://github.com/kataras/iris/blob/master/versioning/README.md) instead.

View File

@ -0,0 +1,73 @@
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/versioning"
)
func main() {
app := iris.New()
examplePerRoute(app)
examplePerParty(app)
// Read the README.md before any action.
app.Run(iris.Addr(":8080"))
}
// How to test:
// Open Postman
// GET: localhost:8080/api/cats
// Headers[1] = Accept-Version: "1" and repeat with
// Headers[1] = Accept-Version: "2.5"
// or even "Accept": "application/json; version=2.5"
func examplePerRoute(app *iris.Application) {
app.Get("/api/cats", versioning.NewMatcher(versioning.Map{
"1": catsVersionExactly1Handler,
">= 2, < 3": catsV2Handler,
versioning.NotFound: versioning.NotFoundHandler,
}))
}
// How to test:
// Open Postman
// GET: localhost:8080/api/users
// Headers[1] = Accept-Version: "1.9.9" and repeat with
// Headers[1] = Accept-Version: "2.5"
//
// POST: localhost:8080/api/users/new
// Headers[1] = Accept-Version: "1.8.3"
//
// POST: localhost:8080/api/users
// Headers[1] = Accept-Version: "2"
func examplePerParty(app *iris.Application) {
usersAPI := app.Party("/api/users")
// version 1.
usersAPIV1 := versioning.NewGroup(">= 1, < 2")
usersAPIV1.Get("/", func(ctx iris.Context) {
ctx.Writef("v1 resource: /api/users handler")
})
usersAPIV1.Post("/new", func(ctx iris.Context) {
ctx.Writef("v1 resource: /api/users/new post handler")
})
// version 2.
usersAPIV2 := versioning.NewGroup(">= 2, < 3")
usersAPIV2.Get("/", func(ctx iris.Context) {
ctx.Writef("v2 resource: /api/users handler")
})
usersAPIV2.Post("/", func(ctx iris.Context) {
ctx.Writef("v2 resource: /api/users post handler")
})
versioning.RegisterGroups(usersAPI, versioning.NotFoundHandler, usersAPIV1, usersAPIV2)
}
func catsVersionExactly1Handler(ctx iris.Context) {
ctx.Writef("v1 exactly resource: /api/cats handler")
}
func catsV2Handler(ctx iris.Context) {
ctx.Writef("v2 resource: /api/cats handler")
}

4
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version
11.0.4
11.1.0
Installation
@ -800,7 +800,7 @@ Standard macro types for parameters:
+------------------------+
path type
anything, should be the last part, can be more than one path segment,
i.e: "/test/*param" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3"
i.e: "/test/{param:path}" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3"
if type is missing then parameter's type is defaulted to string, so
{param} == {param:string}.

1
go.mod
View File

@ -20,6 +20,7 @@ require (
github.com/google/go-querystring v1.0.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/go-version v1.0.0
github.com/imkira/go-interpol v1.1.0 // indirect
github.com/iris-contrib/blackfriday v2.0.0+incompatible
github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f

2
go.sum
View File

@ -42,6 +42,8 @@ github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSf
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=

View File

@ -33,7 +33,7 @@ import (
var (
// Version is the current version number of the Iris Web Framework.
Version = "11.0.4"
Version = "11.1.0"
)
// HTTP status codes as registered with IANA.

1
versioning/AUTHORS Normal file
View File

@ -0,0 +1 @@
Gerasimos Maropoulos <kataras2006@hotmail.com>

27
versioning/LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2018 Gerasimos Maropoulos. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Iris nor the names of its
contributor, Gerasimos Maropoulos, may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

142
versioning/README.md Normal file
View File

@ -0,0 +1,142 @@
# 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
}
})
```

50
versioning/deprecation.go Normal file
View File

@ -0,0 +1,50 @@
package versioning
import (
"time"
"github.com/kataras/iris/context"
)
// DeprecationOptions describes the deprecation headers key-values.
// - "X-API-Warn": options.WarnMessage
// - "X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))
// - "X-API-Deprecation-Info": options.DeprecationInfo
type DeprecationOptions struct {
WarnMessage string
DeprecationDate time.Time
DeprecationInfo string
}
// ShouldHandle reports whether the deprecation headers should be present or no.
func (opts DeprecationOptions) ShouldHandle() bool {
return opts.WarnMessage != "" || !opts.DeprecationDate.IsZero() || opts.DeprecationInfo != ""
}
// DefaultDeprecationOptions are the default deprecation options,
// it defaults the "X-API-Warn" header to a generic message.
var DefaultDeprecationOptions = DeprecationOptions{
WarnMessage: "WARNING! You are using a deprecated version of this API.",
}
// Deprecated marks a specific handler as a deprecated.
// Deprecated can be used to tell the clients that
// a newer version of that specific resource is available instead.
func Deprecated(handler context.Handler, options DeprecationOptions) context.Handler {
if options.WarnMessage == "" {
options.WarnMessage = DefaultDeprecationOptions.WarnMessage
}
return func(ctx context.Context) {
handler(ctx)
ctx.Header("X-API-Warn", options.WarnMessage)
if !options.DeprecationDate.IsZero() {
ctx.Header("X-API-Deprecation-Date", context.FormatTime(ctx, options.DeprecationDate))
}
if options.DeprecationInfo != "" {
ctx.Header("X-API-Deprecation-Info", options.DeprecationInfo)
}
}
}

View File

@ -0,0 +1,33 @@
package versioning_test
import (
"testing"
"time"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
"github.com/kataras/iris/versioning"
)
func TestDeprecated(t *testing.T) {
app := iris.New()
writeVesion := func(ctx iris.Context) {
ctx.WriteString(versioning.GetVersion(ctx))
}
opts := versioning.DeprecationOptions{
WarnMessage: "deprecated, see <this link>",
DeprecationDate: time.Now().UTC(),
DeprecationInfo: "a bigger version is available, see <this link> for more information",
}
app.Get("/", versioning.Deprecated(writeVesion, opts))
e := httptest.New(t, app)
ex := e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect()
ex.Status(iris.StatusOK).Body().Equal("1.0")
ex.Header("X-API-Warn").Equal(opts.WarnMessage)
expectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat())
ex.Header("X-API-Deprecation-Date").Equal(expectedDateStr)
}

185
versioning/group.go Normal file
View File

@ -0,0 +1,185 @@
package versioning
import (
"net/http"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router"
)
type (
vroute struct {
method string
path string
versions Map
}
// Group is a group of version-based routes.
// One version per one or more routes.
Group struct {
version string
extraMethods []string
routes []vroute
deprecation DeprecationOptions
}
)
// NewGroup returns a ptr to Group based on the given "version".
//
// See `Handle` and `RegisterGroups` for more.
func NewGroup(version string) *Group {
return &Group{
version: version,
}
}
// Deprecated marks this group and all its versioned routes
// as deprecated versions of that endpoint.
// It can be called in the end just before `RegisterGroups`
// or first by `NewGroup(...).Deprecated(...)`. It returns itself.
func (g *Group) Deprecated(options DeprecationOptions) *Group {
// if `Deprecated` is called in the end.
for _, r := range g.routes {
r.versions[g.version] = Deprecated(r.versions[g.version], options)
}
// store the options if called before registering any versioned routes.
g.deprecation = options
return g
}
// AllowMethods can be called before `Handle/Get/Post...`
// to tell the underline router that all routes should be registered
// to these "methods" as well.
func (g *Group) AllowMethods(methods ...string) *Group {
g.extraMethods = append(g.extraMethods, methods...)
return g
}
func (g *Group) addVRoute(method, path string, handler context.Handler) {
for _, r := range g.routes { // check if route already exists.
if r.method == method && r.path == path {
return
}
}
g.routes = append(g.routes, vroute{
method: method,
path: path,
versions: Map{g.version: handler},
})
}
// Handle registers a versioned route to the group.
// A call of `RegisterGroups` is necessary in order to register the actual routes
// when the group is complete.
//
// `RegisterGroups` for more.
func (g *Group) Handle(method string, path string, handler context.Handler) {
if g.deprecation.ShouldHandle() { // if `Deprecated` called first.
handler = Deprecated(handler, g.deprecation)
}
methods := append(g.extraMethods, method)
for _, method := range methods {
g.addVRoute(method, path, handler)
}
}
// None registers an "offline" versioned route
// see `context#ExecRoute(routeName)` and routing examples.
func (g *Group) None(path string, handler context.Handler) {
g.Handle(router.MethodNone, path, handler)
}
// Get registers a versioned route for the Get http method.
func (g *Group) Get(path string, handler context.Handler) {
g.Handle(http.MethodGet, path, handler)
}
// Post registers a versioned route for the Post http method.
func (g *Group) Post(path string, handler context.Handler) {
g.Handle(http.MethodPost, path, handler)
}
// Put registers a versioned route for the Put http method
func (g *Group) Put(path string, handler context.Handler) {
g.Handle(http.MethodPut, path, handler)
}
// Delete registers a versioned route for the Delete http method.
func (g *Group) Delete(path string, handler context.Handler) {
g.Handle(http.MethodDelete, path, handler)
}
// Connect registers a versioned route for the Connect http method.
func (g *Group) Connect(path string, handler context.Handler) {
g.Handle(http.MethodConnect, path, handler)
}
// Head registers a versioned route for the Head http method.
func (g *Group) Head(path string, handler context.Handler) {
g.Handle(http.MethodHead, path, handler)
}
// Options registers a versioned route for the Options http method.
func (g *Group) Options(path string, handler context.Handler) {
g.Handle(http.MethodOptions, path, handler)
}
// Patch registers a versioned route for the Patch http method.
func (g *Group) Patch(path string, handler context.Handler) {
g.Handle(http.MethodPatch, path, handler)
}
// Trace registers a versioned route for the Trace http method.
func (g *Group) Trace(path string, handler context.Handler) {
g.Handle(http.MethodTrace, path, handler)
}
// Any registers a versioned route for ALL of the http methods
// (Get,Post,Put,Head,Patch,Options,Connect,Delete).
func (g *Group) Any(registeredPath string, handler context.Handler) {
g.Get(registeredPath, handler)
g.Post(registeredPath, handler)
g.Put(registeredPath, handler)
g.Delete(registeredPath, handler)
g.Connect(registeredPath, handler)
g.Head(registeredPath, handler)
g.Options(registeredPath, handler)
g.Patch(registeredPath, handler)
g.Trace(registeredPath, handler)
}
// RegisterGroups registers one or more groups to an `iris.Party` or to the root router.
// See `NewGroup` and `NotFoundHandler` too.
func RegisterGroups(r router.Party, notFoundHandler context.Handler, groups ...*Group) (actualRoutes []*router.Route) {
var total []vroute
for _, g := range groups {
inner:
for _, r := range g.routes {
for i, tr := range total {
if tr.method == r.method && tr.path == r.path {
total[i].versions[g.version] = r.versions[g.version]
continue inner
}
}
total = append(total, r)
}
}
for _, vr := range total {
if notFoundHandler != nil {
vr.versions[NotFound] = notFoundHandler
}
route := r.Handle(vr.method, vr.path, NewMatcher(vr.versions))
actualRoutes = append(actualRoutes, route)
}
return
}

95
versioning/version.go Normal file
View File

@ -0,0 +1,95 @@
package versioning
import (
"strings"
"github.com/kataras/iris/context"
)
const (
// AcceptVersionHeaderKey is the header key of "Accept-Version".
AcceptVersionHeaderKey = "Accept-Version"
// AcceptHeaderKey is the header key of "Accept".
AcceptHeaderKey = "Accept"
// AcceptHeaderVersionValue is the Accept's header value search term the requested version.
AcceptHeaderVersionValue = "version"
// Key is the context key of the version, can be used to manually modify the "requested" version.
// Example of how you can change the default behavior to extract a requested version (which is by headers)
// from a "version" url parameter instead:
// func(ctx iris.Context) { // &version=1
// ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1"))
// ctx.Next()
// }
Key = "iris.api.version" // for use inside the ctx.Values(), not visible by the user.
// NotFound is the key that can be used inside a `Map` or inside `ctx.Values().Set(versioning.Key, versioning.NotFound)`
// to tell that a version wasn't found, therefore the not found handler should handle the request instead.
NotFound = Key + ".notfound"
)
// NotFoundHandler is the default version not found handler that
// is executed from `NewMatcher` when no version is registered as available to dispatch a resource.
var NotFoundHandler = func(ctx context.Context) {
// 303 is an option too,
// end-dev has the chance to change that behavior by using the NotFound in the map:
//
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
/*
10.5.2 501 Not Implemented
The server does not support the functionality required to fulfill the request.
This is the appropriate response when the server does not
recognize the request method and is not capable of supporting it for any resource.
*/
ctx.StatusCode(501)
ctx.WriteString("version not found")
}
// GetVersion returns the current request version.
//
// 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"
//
// However, the end developer can also set a custom version for a handler via a middleware by using the context's store key
// for versions (see `Key` for further details on that).
func GetVersion(ctx context.Context) string {
// firstly by context store, if manually set-ed by a middleware.
if version := ctx.Values().GetString(Key); version != "" {
return version
}
// secondly by the "Accept-Version" header.
if version := ctx.GetHeader(AcceptVersionHeaderKey); version != "" {
return version
}
// thirdly by the "Accept" header which is like"...; version=1.0"
acceptValue := ctx.GetHeader(AcceptHeaderKey)
if acceptValue != "" {
if idx := strings.Index(acceptValue, AcceptHeaderVersionValue); idx != -1 {
rem := acceptValue[idx:]
startVersion := strings.Index(rem, "=")
if startVersion == -1 || len(rem) < startVersion+1 {
return NotFound
}
rem = rem[startVersion+1:]
end := strings.Index(rem, " ")
if end == -1 {
end = strings.Index(rem, ";")
}
if end == -1 {
end = len(rem)
}
if version := rem[:end]; version != "" {
return version
}
}
}
return NotFound
}

View File

@ -0,0 +1,48 @@
package versioning_test
import (
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
"github.com/kataras/iris/versioning"
)
func TestGetVersion(t *testing.T) {
app := iris.New()
writeVesion := func(ctx iris.Context) {
ctx.WriteString(versioning.GetVersion(ctx))
}
app.Get("/", writeVesion)
app.Get("/manual", func(ctx iris.Context) {
ctx.Values().Set(versioning.Key, "11.0.5")
ctx.Next()
}, writeVesion)
e := httptest.New(t, app)
e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect().
Status(iris.StatusOK).Body().Equal("1.0")
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1").Expect().
Status(iris.StatusOK).Body().Equal("2.1")
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1 ;other=dsa").Expect().
Status(iris.StatusOK).Body().Equal("2.1")
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=2.1").Expect().
Status(iris.StatusOK).Body().Equal("2.1")
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=1").Expect().
Status(iris.StatusOK).Body().Equal("1")
// unknown versions.
e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "").Expect().
Status(iris.StatusOK).Body().Equal(versioning.NotFound)
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=").Expect().
Status(iris.StatusOK).Body().Equal(versioning.NotFound)
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version= ;other=dsa").Expect().
Status(iris.StatusOK).Body().Equal(versioning.NotFound)
e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=").Expect().
Status(iris.StatusOK).Body().Equal(versioning.NotFound)
e.GET("/manual").Expect().Status(iris.StatusOK).Body().Equal("11.0.5")
}

120
versioning/versioning.go Normal file
View File

@ -0,0 +1,120 @@
package versioning
import (
"github.com/kataras/iris/context"
"github.com/hashicorp/go-version"
)
// If reports whether the "version" is matching to the "is".
// the "is" can be a constraint like ">= 1, < 3".
func If(v string, is string) bool {
ver, err := version.NewVersion(v)
if err != nil {
return false
}
constraints, err := version.NewConstraint(is)
if err != nil {
return false
}
return constraints.Check(ver)
}
// Match acts exactly the same as `If` does but instead it accepts
// a Context, so it can be called by a handler to determinate the requested version.
func Match(ctx context.Context, expectedVersion string) bool {
return If(GetVersion(ctx), expectedVersion)
}
// Map is a map of versions targets to a handlers,
// a handler per version or constraint, the key can be something like ">1, <=2" or just "1".
type Map map[string]context.Handler
// NewMatcher creates a single handler which decides what handler
// should be executed based on the requested version.
//
// Use the `NewGroup` if you want to add many routes under a specific version.
//
// See `Map` and `NewGroup` too.
func NewMatcher(versions Map) context.Handler {
constraintsHandlers, notFoundHandler := buildConstraints(versions)
return func(ctx context.Context) {
versionString := GetVersion(ctx)
if versionString == NotFound {
notFoundHandler(ctx)
return
}
ver, err := version.NewVersion(versionString)
if err != nil {
notFoundHandler(ctx)
return
}
for _, ch := range constraintsHandlers {
if ch.constraints.Check(ver) {
ctx.Header("X-API-Version", ver.String())
ch.handler(ctx)
return
}
}
// pass the not matched version so the not found handler can have knowedge about it.
// ctx.Values().Set(Key, versionString)
// or let a manual cal of GetVersion(ctx) do that instead.
notFoundHandler(ctx)
}
}
type constraintsHandler struct {
constraints version.Constraints
handler context.Handler
}
func buildConstraints(versionsHandler Map) (constraintsHandlers []*constraintsHandler, notfoundHandler context.Handler) {
for v, h := range versionsHandler {
if v == NotFound {
notfoundHandler = h
continue
}
constraints, err := version.NewConstraint(v)
if err != nil {
panic(err)
}
constraintsHandlers = append(constraintsHandlers, &constraintsHandler{
constraints: constraints,
handler: h,
})
}
if notfoundHandler == nil {
notfoundHandler = NotFoundHandler
}
// no sort, the end-dev should declare
// all version constraint, i.e < 4.0 may be catch 1.0 if not something like
// >= 3.0, < 4.0.
// I can make it ordered but I do NOT like the final API of it:
/*
app.Get("/api/user", NewMatcher( // accepts an array, ordered, see last elem.
V("1.0", vHandler("v1 here")),
V("2.0", vHandler("v2 here")),
V("< 4.0", vHandler("v3.x here")),
))
instead we have:
app.Get("/api/user", NewMatcher(Map{ // accepts a map, unordered, see last elem.
"1.0": Deprecated(vHandler("v1 here")),
"2.0": vHandler("v2 here"),
">= 3.0, < 4.0": vHandler("v3.x here"),
VersionUnknown: customHandlerForNotMatchingVersion,
}))
*/
return
}

View File

@ -0,0 +1,136 @@
package versioning_test
import (
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
"github.com/kataras/iris/versioning"
)
func notFoundHandler(ctx iris.Context) {
ctx.NotFound()
}
const (
v10Response = "v1.0 handler"
v2Response = "v2.x handler"
)
func sendHandler(contents string) iris.Handler {
return func(ctx iris.Context) {
ctx.WriteString(contents)
}
}
func TestIf(t *testing.T) {
if expected, got := true, versioning.If("1.0", ">=1"); expected != got {
t.Fatalf("expected %s to be %s", "1.0", ">= 1")
}
if expected, got := true, versioning.If("1.2.3", "> 1.2"); expected != got {
t.Fatalf("expected %s to be %s", "1.2.3", "> 1.2")
}
}
func TestNewMatcher(t *testing.T) {
app := iris.New()
userAPI := app.Party("/api/user")
userAPI.Get("/", versioning.NewMatcher(versioning.Map{
"1.0": sendHandler(v10Response),
">= 2, < 3": sendHandler(v2Response),
versioning.NotFound: notFoundHandler,
}))
// middleware as usual.
myMiddleware := func(ctx iris.Context) {
ctx.Header("X-Custom", "something")
ctx.Next()
}
myVersions := versioning.Map{
"1.0": sendHandler(v10Response),
}
userAPI.Get("/with_middleware", myMiddleware, versioning.NewMatcher(myVersions))
e := httptest.New(t, app)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect().
Status(iris.StatusOK).Body().Equal(v10Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
// middleware as usual.
ex := e.GET("/api/user/with_middleware").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect()
ex.Status(iris.StatusOK).Body().Equal(v10Response)
ex.Header("X-Custom").Equal("something")
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect().
Status(iris.StatusNotFound).Body().Equal("Not Found")
}
func TestNewGroup(t *testing.T) {
app := iris.New()
userAPI := app.Party("/api/user")
// [... static serving, middlewares and etc goes here].
userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions)
// V10middlewareResponse := "m1"
// userAPIV10.Use(func(ctx iris.Context) {
// println("exec userAPIV10.Use - midl1")
// sendHandler(V10middlewareResponse)(ctx)
// ctx.Next()
// })
// userAPIV10.Use(func(ctx iris.Context) {
// println("exec userAPIV10.Use - midl2")
// sendHandler(V10middlewareResponse + "midl2")(ctx)
// ctx.Next()
// })
// userAPIV10.Use(func(ctx iris.Context) {
// println("exec userAPIV10.Use - midl3")
// ctx.Next()
// })
userAPIV10.Get("/", sendHandler(v10Response))
userAPIV2 := versioning.NewGroup(">= 2, < 3")
// V2middlewareResponse := "m2"
// userAPIV2.Use(func(ctx iris.Context) {
// println("exec userAPIV2.Use - midl1")
// sendHandler(V2middlewareResponse)(ctx)
// ctx.Next()
// })
// userAPIV2.Use(func(ctx iris.Context) {
// println("exec userAPIV2.Use - midl2")
// ctx.Next()
// })
userAPIV2.Get("/", sendHandler(v2Response))
userAPIV2.Post("/", sendHandler(v2Response))
userAPIV2.Put("/other", sendHandler(v2Response))
versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2)
e := httptest.New(t, app)
ex := e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect()
ex.Status(iris.StatusOK).Body().Equal(v10Response)
ex.Header("X-API-Warn").Equal(versioning.DefaultDeprecationOptions.WarnMessage)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.POST("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.PUT("/api/user/other").WithHeader(versioning.AcceptVersionHeaderKey, "2.9").Expect().
Status(iris.StatusOK).Body().Equal(v2Response)
e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect().
Status(iris.StatusNotImplemented).Body().Equal("version not found")
}