From d19672115b22be3cf8eac7561c92493c510d8cba Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Wed, 6 May 2020 07:17:24 +0300 Subject: [PATCH] add Context.SetVersion helper Former-commit-id: 605d6c1e78f73b8f2c89bd2dc7ee23f21551d47b --- HISTORY.md | 2 ++ configuration.go | 15 +++++++++++++++ context/configuration.go | 4 +++- context/context.go | 10 +++++++++- versioning/version.go | 21 ++++++++++++--------- versioning/version_test.go | 10 +++++----- versioning/versioning.go | 4 ++-- 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 36b386fd..260a261e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -394,6 +394,7 @@ Other Improvements: New Context Methods: +- `Context.SetVersion(constraint string)` force-sets an [API Version](https://github.com/kataras/iris/wiki/API-versioning) - `Context.SetLanguage(langCode string)` force-sets a language code from inside a middleare, similar to the `app.I18n.ExtractFunc` - `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the "download" speed of the client - `Context.IsHTTP2() bool` reports whether the protocol version for incoming request was HTTP/2 @@ -416,6 +417,7 @@ New Context Methods: Breaking Changes: +- `versioning.GetVersion` now returns an empty string if version wasn't found. - Change the MIME type of `Javascript .js` and `JSONP` as the HTML specification now recommends to `"text/javascript"` instead of the obselete `"application/javascript"`. This change was pushed to the `Go` language itself as well. See . - Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.Gzip)`) or a prior call to `Context.Gzip(true)` will enable gzip compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed). - `Context.ServeContent` no longer returns an error, see `ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` new methods too. diff --git a/configuration.go b/configuration.go index 985974ce..44bf27a7 100644 --- a/configuration.go +++ b/configuration.go @@ -856,6 +856,10 @@ type Configuration struct { // See `i18n.ExtractFunc` for a more organised way of the same feature. // Defaults to "iris.locale.language". LanguageContextKey string `json:"languageContextKey,omitempty" yaml:"LanguageContextKey" toml:"LanguageContextKey"` + // VersionContextKey is the context key which an API Version can be modified + // via a middleware through `SetVersion` method, e.g. `ctx.SetVersion("1.0, 1.1")`. + // Defauls to "iris.api.version". + VersionContextKey string `json:"versionContextKey" yaml:"VersionContextKey" toml:"VersionContextKey"` // GetViewLayoutContextKey is the key of the context's user values' key // which is being used to set the template // layout from a middleware or the main handler. @@ -1014,6 +1018,12 @@ func (c Configuration) GetLanguageContextKey() string { return c.LanguageContextKey } +// GetVersionContextKey returns the configuration's VersionContextKey value, +// used for API Versioning. +func (c Configuration) GetVersionContextKey() string { + return c.VersionContextKey +} + // GetViewLayoutContextKey returns the key of the context's user values' key // which is being used to set the template // layout from a middleware or the main handler. @@ -1152,6 +1162,10 @@ func WithConfiguration(c Configuration) Configurator { main.LanguageContextKey = v } + if v := c.VersionContextKey; v != "" { + main.VersionContextKey = v + } + if v := c.ViewLayoutContextKey; v != "" { main.ViewLayoutContextKey = v } @@ -1206,6 +1220,7 @@ func DefaultConfiguration() Configuration { PostMaxMemory: 32 << 20, // 32MB LocaleContextKey: "iris.locale", LanguageContextKey: "iris.locale.language", + VersionContextKey: "iris.api.version", ViewLayoutContextKey: "iris.viewLayout", ViewDataContextKey: "iris.viewData", RemoteAddrHeaders: make(map[string]bool), diff --git a/context/configuration.go b/context/configuration.go index 2d9e8f1a..e9735180 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -81,7 +81,9 @@ type ConfigurationReadOnly interface { // GetLanguageContextKey returns the configuration's LanguageContextKey value, // used for i18n. Defaults to "iris.locale.language". GetLanguageContextKey() string - + // GetVersionContextKey returns the configuration's VersionKey value, + // used for API Versioning. Defaults to "iris.api.version". + GetVersionContextKey() string // GetViewLayoutContextKey returns the key of the context's user values' key // which is being used to set the template // layout from a middleware or the main handler. diff --git a/context/context.go b/context/context.go index 9db59000..ac6c3a6f 100644 --- a/context/context.go +++ b/context/context.go @@ -424,7 +424,9 @@ type Context interface { // See `GetLocale` too. // Example: https://github.com/kataras/iris/tree/master/_examples/i18n Tr(format string, args ...interface{}) string - + // SetVersion force-sets the API Version integrated with the "iris/versioning" subpackage. + // It can be used inside a middleare. + SetVersion(constraint string) // +------------------------------------------------------------+ // | Headers helpers | // +------------------------------------------------------------+ @@ -2025,6 +2027,12 @@ func (ctx *context) Tr(format string, args ...interface{}) string { // other nam return fmt.Sprintf(format, args...) } +// SetVersion force-sets the API Version integrated with the "iris/versioning" subpackage. +// It can be used inside a middleare. +func (ctx *context) SetVersion(constraint string) { + ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint) +} + // +------------------------------------------------------------+ // | Response Headers helpers | // +------------------------------------------------------------+ diff --git a/versioning/version.go b/versioning/version.go index 6ca83ad4..a72d6de9 100644 --- a/versioning/version.go +++ b/versioning/version.go @@ -21,10 +21,12 @@ const ( // 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)` + // + // DEPRECATED: May 06 2020: Use `ctx.SetVersion(ctx.URLParamDefault("version", "1"))` instead. + Key = "iris.api.version" + // NotFound is the key that can be used inside a `Map` or inside `ctx.SetVersion(versioning.NotFound)` // to tell that a version wasn't found, therefore the not found handler should handle the request instead. - NotFound = Key + ".notfound" + NotFound = "iris.api.version.notfound" ) // NotFoundHandler is the default version not found handler that @@ -55,13 +57,14 @@ var NotFoundHandler = func(ctx context.Context) { // 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 != "" { + // firstly by context store, if manually set by a middleware. + version := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetVersionContextKey()) + if version != "" { return version } // secondly by the "Accept-Version" header. - if version := ctx.GetHeader(AcceptVersionHeaderKey); version != "" { + if version = ctx.GetHeader(AcceptVersionHeaderKey); version != "" { return version } @@ -72,7 +75,7 @@ func GetVersion(ctx context.Context) string { rem := acceptValue[idx:] startVersion := strings.Index(rem, "=") if startVersion == -1 || len(rem) < startVersion+1 { - return NotFound + return "" } rem = rem[startVersion+1:] @@ -85,11 +88,11 @@ func GetVersion(ctx context.Context) string { end = len(rem) } - if version := rem[:end]; version != "" { + if version = rem[:end]; version != "" { return version } } } - return NotFound + return "" } diff --git a/versioning/version_test.go b/versioning/version_test.go index 20389e7c..e5aef53f 100644 --- a/versioning/version_test.go +++ b/versioning/version_test.go @@ -17,7 +17,7 @@ func TestGetVersion(t *testing.T) { app.Get("/", writeVesion) app.Get("/manual", func(ctx iris.Context) { - ctx.Values().Set(versioning.Key, "11.0.5") + ctx.SetVersion("11.0.5") ctx.Next() }, writeVesion) @@ -36,13 +36,13 @@ func TestGetVersion(t *testing.T) { // unknown versions. e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "").Expect(). - Status(iris.StatusOK).Body().Equal(versioning.NotFound) + Status(iris.StatusOK).Body().Equal("") e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=").Expect(). - Status(iris.StatusOK).Body().Equal(versioning.NotFound) + Status(iris.StatusOK).Body().Equal("") e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version= ;other=dsa").Expect(). - Status(iris.StatusOK).Body().Equal(versioning.NotFound) + Status(iris.StatusOK).Body().Equal("") e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=").Expect(). - Status(iris.StatusOK).Body().Equal(versioning.NotFound) + Status(iris.StatusOK).Body().Equal("") e.GET("/manual").Expect().Status(iris.StatusOK).Body().Equal("11.0.5") } diff --git a/versioning/versioning.go b/versioning/versioning.go index fd90381f..36f52ea8 100644 --- a/versioning/versioning.go +++ b/versioning/versioning.go @@ -43,7 +43,7 @@ func NewMatcher(versions Map) context.Handler { return func(ctx context.Context) { versionString := GetVersion(ctx) - if versionString == NotFound { + if versionString == "" || versionString == NotFound { notFoundHandler(ctx) return } @@ -63,7 +63,7 @@ func NewMatcher(versions Map) context.Handler { } // pass the not matched version so the not found handler can have knowedge about it. - // ctx.Values().Set(Key, versionString) + // ctx.SetVersion(versionString) // or let a manual cal of GetVersion(ctx) do that instead. notFoundHandler(ctx) }