From b4365cee8d644c784961e6880bd61881973f5362 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Thu, 7 May 2020 01:14:41 +0300 Subject: [PATCH] sessions: add GobTranscoder and document the DefaultTranscoder package-level variable Former-commit-id: 631d52bb6d0eb0dbe2e7416517ec019c5902ca71 --- _examples/dependency-injection/basic/main.go | 9 +- _examples/sessions/database/boltdb/main.go | 23 +++++ sessions/sessiondb/boltdb/database.go | 2 +- sessions/transcoding.go | 92 ++++++++++++++++++-- 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/_examples/dependency-injection/basic/main.go b/_examples/dependency-injection/basic/main.go index f19a091c..58138fa5 100644 --- a/_examples/dependency-injection/basic/main.go +++ b/_examples/dependency-injection/basic/main.go @@ -1,10 +1,14 @@ package main -import "github.com/kataras/iris/v12" +import ( + "github.com/kataras/iris/v12" + + "github.com/go-playground/validator/v10" +) type ( testInput struct { - Email string `json:"email"` + Email string `json:"email" validate:"required"` } testOutput struct { @@ -35,6 +39,7 @@ func configureAPI(api *iris.APIContainer) { func main() { app := iris.New() + app.Validator = validator.New() app.Logger().SetLevel("debug") app.ConfigureContainer(configureAPI) diff --git a/_examples/sessions/database/boltdb/main.go b/_examples/sessions/database/boltdb/main.go index 36ac4b45..23dc727b 100644 --- a/_examples/sessions/database/boltdb/main.go +++ b/_examples/sessions/database/boltdb/main.go @@ -30,6 +30,28 @@ func main() { AllowReclaim: true, }) + // The default database's values encoder and decoder + // calls the value's `Marshal/Unmarshal` methods (if any) + // otherwise JSON is selected, + // the JSON format can be stored to any database and + // it supports both builtin language types(e.g. string, int) and custom struct values. + // Also, and the most important, the values can be + // retrieved/logged/monitored by a third-party program + // written in any other language as well. + // + // You can change this behavior by registering a custom `Transcoder`. + // Iris provides a `GobTranscoder` which is mostly suitable + // if your session values are going to be custom Go structs. + // Select this if you always retrieving values through Go. + // Don't forget to initialize a call of gob.Register when necessary. + // Read https://golang.org/pkg/encoding/gob/ for more. + // + // You can also implement your own `sessions.Transcoder` and use it, + // i.e: a transcoder which will allow(on Marshal: return its byte representation and nil error) + // or dissalow(on Marshal: return non nil error) certain types. + // + // sessions.DefaultTranscoder = sessions.GobTranscoder{} + // // IMPORTANT: // @@ -37,6 +59,7 @@ func main() { // the rest of the code stays the same. app := iris.New() + app.Logger().SetLevel("debug") app.Get("/", func(ctx iris.Context) { ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead") diff --git a/sessions/sessiondb/boltdb/database.go b/sessions/sessiondb/boltdb/database.go index 5dc63746..90e884b8 100644 --- a/sessions/sessiondb/boltdb/database.go +++ b/sessions/sessiondb/boltdb/database.go @@ -288,7 +288,7 @@ func (db *Database) Get(sid string, key string) (value interface{}) { return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value) }) if err != nil { - golog.Debugf("session '%s' key '%s' not found", sid, key) + golog.Debugf("session '%s' key '%s' cannot be retrieved: %v", sid, key, err) } return diff --git a/sessions/transcoding.go b/sessions/transcoding.go index 613e2d46..00d1db2c 100644 --- a/sessions/transcoding.go +++ b/sessions/transcoding.go @@ -1,6 +1,11 @@ package sessions -import "encoding/json" +import ( + "bytes" + "encoding/gob" + "encoding/json" + "reflect" +) type ( // Marshaler is the common marshaler interface, used by transcoder. @@ -18,13 +23,43 @@ type ( } ) -// DefaultTranscoder is the default transcoder across databases, it's the JSON by default. -// Change it if you want a different serialization/deserialization inside your session databases (when `UseDatabase` is used). -var DefaultTranscoder Transcoder = defaultTranscoder{} +var ( + _ Transcoder = (*defaultTranscoder)(nil) + _ Transcoder = (*GobTranscoder)(nil) -type defaultTranscoder struct{} + // DefaultTranscoder is the default transcoder across databases (when `UseDatabase` is used). + // + // The default database's values encoder and decoder + // calls the value's `Marshal/Unmarshal` methods (if any) + // otherwise JSON is selected, + // the JSON format can be stored to any database and + // it supports both builtin language types(e.g. string, int) and custom struct values. + // Also, and the most important, the values can be + // retrieved/logged/monitored by a third-party program + // written in any other language as well. + // + // You can change this behavior by registering a custom `Transcoder`. + // Iris provides a `GobTranscoder` which is mostly suitable + // if your session values are going to be custom Go structs. + // Select this if you always retrieving values through Go. + // Don't forget to initialize a call of gob.Register when necessary. + // Read https://golang.org/pkg/encoding/gob/ for more. + // + // You can also implement your own `sessions.Transcoder` and use it, + // i.e: a transcoder which will allow(on Marshal: return its byte representation and nil error) + // or dissalow(on Marshal: return non nil error) certain types. + // + // sessions.DefaultTranscoder = sessions.GobTranscoder{} + DefaultTranscoder Transcoder = defaultTranscoder{} +) -func (d defaultTranscoder) Marshal(value interface{}) ([]byte, error) { +type ( + defaultTranscoder struct{} + // GobTranscoder can be set to `DefaultTranscoder` to modify the database(s) transcoder. + GobTranscoder struct{} +) + +func (defaultTranscoder) Marshal(value interface{}) ([]byte, error) { if tr, ok := value.(Marshaler); ok { return tr.Marshal(value) } @@ -36,7 +71,7 @@ func (d defaultTranscoder) Marshal(value interface{}) ([]byte, error) { return json.Marshal(value) } -func (d defaultTranscoder) Unmarshal(b []byte, outPtr interface{}) error { +func (defaultTranscoder) Unmarshal(b []byte, outPtr interface{}) error { if tr, ok := outPtr.(Unmarshaler); ok { return tr.Unmarshal(b, outPtr) } @@ -47,3 +82,46 @@ func (d defaultTranscoder) Unmarshal(b []byte, outPtr interface{}) error { return json.Unmarshal(b, outPtr) } + +// Marshal returns the gob encoding of "value". +func (GobTranscoder) Marshal(value interface{}) ([]byte, error) { + var ( + w = new(bytes.Buffer) + enc = gob.NewEncoder(w) + err error + ) + + switch v := value.(type) { + case reflect.Value: + err = enc.EncodeValue(v) + case string, + int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, + float32, float64, + complex64, complex128: + err = enc.Encode(&v) + default: + err = enc.Encode(value) + } + + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +// Unmarshal parses the gob-encoded data "b" and stores the result +// in the value pointed to by "outPtr". +func (GobTranscoder) Unmarshal(b []byte, outPtr interface{}) error { + var ( + r = bytes.NewBuffer(b) + dec = gob.NewDecoder(r) + ) + + if v, ok := outPtr.(reflect.Value); ok { + return dec.DecodeValue(v) + } + + return dec.Decode(outPtr) +}