add a new simple, builtin requestid middleware (makes use of the Context.SetID/GetID methods too)

Former-commit-id: d46bce7c1964adada01934aa95daf389c141defc
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-05-28 16:20:58 +03:00
parent d556cfc39a
commit 9e5672da25
13 changed files with 170 additions and 21 deletions

View File

@ -380,7 +380,7 @@ Other Improvements:
- Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie) example has been updated. - Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie) example has been updated.
- `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/master/_examples/cookies/options). - `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/master/_examples/cookies/options).
- `iris.TLS` can now accept certificates as raw contents too. - `iris.TLS` can now accept certificates in form of raw `[]byte` contents too.
- `iris.TLS` registers a secondary http server which redirects "http://" to their "https://" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS` (or `iris.AutoTLS`), e.g. `app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))`. - `iris.TLS` registers a secondary http server which redirects "http://" to their "https://" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS` (or `iris.AutoTLS`), e.g. `app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))`.
- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code. - Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.

10
NOTICE
View File

@ -37,10 +37,7 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911
d132d2847a d132d2847a
go-version 2b13044f5cdd383 https://github.com/hashicorp/go-version go-version 2b13044f5cdd383 https://github.com/hashicorp/go-version
3370d41ce57d8bf 3370d41ce57d8bf
3cec5e62b8 3cec5e62b8
go.uuid 36e9d2ebbde5e3f https://github.com/iris-contrib/go.uuid
13ab2e25625fd45
3271d6522e
golog 4038abff49248cc https://github.com/kataras/golog golog 4038abff49248cc https://github.com/kataras/golog
fe2043e2e763923 fe2043e2e763923
d612998bea d612998bea
@ -97,4 +94,7 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911
b7bbc7f005 b7bbc7f005
jose d84c719419c2a90 https://github.com/square/go-jose jose d84c719419c2a90 https://github.com/square/go-jose
8d188ea67e09652 8d188ea67e09652
f5c1929ae8 f5c1929ae8
uuid cb32006e483f2a2 https://github.com/google/uuid
3230e24209cf185
c65b477dbf

View File

@ -9,6 +9,6 @@ func main() {
}) })
// Read index.html comments, // Read index.html comments,
// adn then start and navigate to http://localhost:8080. // and then start and navigate to http://localhost:8080.
app.Listen(":8080") app.Listen(":8080")
} }

View File

@ -3,7 +3,7 @@ package main
import ( import (
"net/url" "net/url"
"github.com/iris-contrib/go.uuid" "github.com/google/uuid"
) )
// Generator the type to generate keys(short urls) // Generator the type to generate keys(short urls)
@ -11,7 +11,7 @@ type Generator func() string
// DefaultGenerator is the defautl url generator // DefaultGenerator is the defautl url generator
var DefaultGenerator = func() string { var DefaultGenerator = func() string {
id, _ := uuid.NewV4() id, _ := uuid.NewRandom()
return id.String() return id.String()
} }

View File

@ -3,7 +3,7 @@
// Article: https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7 // Article: https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7
// //
// $ go get go.etcd.io/bbolt/... // $ go get go.etcd.io/bbolt/...
// $ go get github.com/iris-contrib/go.uuid // $ go get github.com/google/uuid
// $ cd $GOPATH/src/github.com/kataras/iris/_examples/tutorial/url-shortener // $ cd $GOPATH/src/github.com/kataras/iris/_examples/tutorial/url-shortener
// $ go build // $ go build
// $ ./url-shortener // $ ./url-shortener

View File

@ -1149,14 +1149,15 @@ type Context interface {
// and methods are not available here for the developer's safety. // and methods are not available here for the developer's safety.
Application() Application Application() Application
// SetID sets an ID, any value, to the Context. // SetID sets an ID, any value, to the Request Context.
// If possible the "id" should implement a `String() string` method // If possible the "id" should implement a `String() string` method
// so it can be rendered on `Context.String` method. // so it can be rendered on `Context.String` method.
// //
// See `GetID` too. // See `GetID` and `middleware/requestid` too.
SetID(id interface{}) SetID(id interface{})
// GetID returns the Context's ID. // GetID returns the Request Context's ID.
// It returns nil if not given by a prior `SetID` call. // It returns nil if not given by a prior `SetID` call.
// See `middleware/requestid` too.
GetID() interface{} GetID() interface{}
// String returns the string representation of this request. // String returns the string representation of this request.
// //
@ -5518,17 +5519,18 @@ func (ctx *context) Application() Application {
const idContextKey = "iris.context.id" const idContextKey = "iris.context.id"
// SetID sets an ID, any value, to the Context. // SetID sets an ID, any value, to the Request Context.
// If possible the "id" should implement a `String() string` method // If possible the "id" should implement a `String() string` method
// so it can be rendered on `Context.String` method. // so it can be rendered on `Context.String` method.
// //
// See `GetID` too. // See `GetID` and `middleware/requestid` too.
func (ctx *context) SetID(id interface{}) { func (ctx *context) SetID(id interface{}) {
ctx.values.Set(idContextKey, id) ctx.values.Set(idContextKey, id)
} }
// GetID returns the Context's ID. // GetID returns the Request Context's ID.
// It returns nil if not given by a prior `SetID` call. // It returns nil if not given by a prior `SetID` call.
// See `middleware/requestid` too.
func (ctx *context) GetID() interface{} { func (ctx *context) GetID() interface{} {
return ctx.values.Get(idContextKey) return ctx.values.Get(idContextKey)
} }

2
go.mod
View File

@ -12,9 +12,9 @@ require (
github.com/fatih/structs v1.1.0 github.com/fatih/structs v1.1.0
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/gomodule/redigo v1.8.1 github.com/gomodule/redigo v1.8.1
github.com/google/uuid v1.1.2-0.20200519141726-cb32006e483f
github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/go-version v1.2.0
github.com/iris-contrib/blackfriday v2.0.0+incompatible github.com/iris-contrib/blackfriday v2.0.0+incompatible
github.com/iris-contrib/go.uuid v2.0.0+incompatible
github.com/iris-contrib/httpexpect/v2 v2.0.5 github.com/iris-contrib/httpexpect/v2 v2.0.5
github.com/iris-contrib/jade v1.1.4 github.com/iris-contrib/jade v1.1.4
github.com/iris-contrib/pongo2 v0.0.1 github.com/iris-contrib/pongo2 v0.0.1

View File

@ -11,6 +11,8 @@ Builtin Handlers
| [hCaptcha](hcaptcha) | [iris/_examples/miscellaneous/recaptcha](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/hcaptcha) | | [hCaptcha](hcaptcha) | [iris/_examples/miscellaneous/recaptcha](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/hcaptcha) |
| [recovery](recover) | [iris/_examples/miscellaneous/recover](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recover) | | [recovery](recover) | [iris/_examples/miscellaneous/recover](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recover) |
| [rate](rate) | [iris/_examples/miscellaneous/ratelimit](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/ratelimit) | | [rate](rate) | [iris/_examples/miscellaneous/ratelimit](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/ratelimit) |
| [jwt](jwt) | [iris/_examples/miscellaneous/jwt](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/jwt) |
| [requestid](requestid) | [iris/middleware/requestid/requestid_test.go](https://github.com/kataras/iris/blob/master/_examples/middleware/requestid/requestid_test.go) |
Community made Community made
------------ ------------

View File

@ -106,7 +106,7 @@ type JWT struct {
VerificationKey interface{} VerificationKey interface{}
// Encrypter is used to, optionally, encrypt the token. // Encrypter is used to, optionally, encrypt the token.
// It is set on `WithExpiration` method. // It is set on `WithEncryption` method.
Encrypter jose.Encrypter Encrypter jose.Encrypter
// DecriptionKey is used to decrypt the token (private key) // DecriptionKey is used to decrypt the token (private key)
DecriptionKey interface{} DecriptionKey interface{}

View File

@ -0,0 +1,82 @@
package requestid
import (
"github.com/kataras/iris/v12/context"
"github.com/google/uuid"
)
func init() {
context.SetHandlerName("iris/middleware/requestid.*", "iris.request.id")
}
const xRequestIDHeaderValue = "X-Request-ID"
// Generator defines the function which should extract or generate
// a Request ID. See `DefaultGenerator` and `New` package-level functions.
type Generator func(ctx context.Context) string
// DefaultGenerator is the default `Generator` that is used
// when nil is passed on `New` package-level function.
// It extracts the ID from the "X-Request-ID" request header value
// or, if missing, it generates a new UUID(v4) and sets the header and context value.
//
// See `Get` package-level function too.
var DefaultGenerator Generator = func(ctx context.Context) string {
id := ctx.GetHeader(xRequestIDHeaderValue)
if id == "" {
uid, err := uuid.NewRandom()
if err != nil {
ctx.StopWithStatus(500)
return ""
}
id = uid.String()
ctx.Header(xRequestIDHeaderValue, id)
}
return id
}
// New returns a new request id middleware.
// It accepts an ID Generator.
// The Generator can stop the handlers chain with an error or
// return a valid ID (string).
// If it's nil then the `DefaultGenerator` will be used instead.
func New(gen Generator) context.Handler {
if gen == nil {
gen = DefaultGenerator
}
return func(ctx context.Context) {
if Get(ctx) != "" {
ctx.Next()
return
}
id := gen(ctx)
if ctx.IsStopped() {
// ctx.Next checks that
// but we don't want to call SetID if generator failed.
return
}
ctx.SetID(id)
ctx.Next()
}
}
// Get returns the Request ID or empty string.
//
// A shortcut of `context.GetID().(string)`.
func Get(ctx context.Context) string {
v := ctx.GetID()
if v != nil {
if id, ok := v.(string); ok {
return id
}
}
return ""
}

View File

@ -0,0 +1,63 @@
package requestid_test
import (
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
"github.com/kataras/iris/v12/middleware/requestid"
)
func TestRequestID(t *testing.T) {
app := iris.New()
h := func(ctx iris.Context) {
ctx.WriteString(requestid.Get(ctx))
}
def := app.Party("/default")
{
def.Use(requestid.New(nil))
def.Get("/", h)
}
const expectedCustomID = "my_id"
custom := app.Party("/custom")
{
customGen := func(ctx context.Context) string {
return expectedCustomID
}
custom.Use(requestid.New(customGen))
custom.Get("/", h)
}
const expectedErrMsg = "no id"
customWithErr := app.Party("/custom_err")
{
customGen := func(ctx context.Context) string {
ctx.StopWithText(iris.StatusUnauthorized, expectedErrMsg)
return ""
}
customWithErr.Use(requestid.New(customGen))
customWithErr.Get("/", h)
}
const expectedCustomIDFromOtherMiddleware = "my custom id"
changeID := app.Party("/custom_change_id")
{
changeID.Use(func(ctx iris.Context) {
ctx.SetID(expectedCustomIDFromOtherMiddleware)
ctx.Next()
})
changeID.Use(requestid.New(nil))
changeID.Get("/", h)
}
e := httptest.New(t, app)
e.GET("/default").Expect().Status(httptest.StatusOK).Body().NotEmpty()
e.GET("/custom").Expect().Status(httptest.StatusOK).Body().Equal(expectedCustomID)
e.GET("/custom_err").Expect().Status(httptest.StatusUnauthorized).Body().Equal(expectedErrMsg)
e.GET("/custom_change_id").Expect().Status(httptest.StatusOK).Body().Equal(expectedCustomIDFromOtherMiddleware)
}

View File

@ -5,7 +5,7 @@ import (
"github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/context"
uuid "github.com/iris-contrib/go.uuid" "github.com/google/uuid"
) )
const ( const (
@ -71,7 +71,7 @@ func (c Config) Validate() Config {
if c.SessionIDGenerator == nil { if c.SessionIDGenerator == nil {
c.SessionIDGenerator = func(context.Context) string { c.SessionIDGenerator = func(context.Context) string {
id, _ := uuid.NewV4() id, _ := uuid.NewRandom()
return id.String() return id.String()
} }
} }

View File

@ -15,7 +15,7 @@ func init() {
// on a Context, which returns a *Session, type. // on a Context, which returns a *Session, type.
// It performs automatic memory cleanup on expired sessions. // It performs automatic memory cleanup on expired sessions.
// It can accept a `Database` for persistence across server restarts. // It can accept a `Database` for persistence across server restarts.
// A session can set temporarly values (flash messages). // A session can set temporary values (flash messages).
type Sessions struct { type Sessions struct {
config Config config Config
provider *provider provider *provider