Method 1: Reimplement the echo.Context.Get and echo.Context.Set methods to manipulate the ctx.Request().Context() object.
Disadvantages: http.Request.WithContext will be called once for each Set method, and *http.Request will be copied once. See the implementation of WithContext method for details.
Method 2: Reimplement the echo.Context.Get and echo.Context.Set methods to manipulate the contextValueData2 object, and set http.Request.WithContext to a custom context.Context contextValueData2.
Disadvantages: Before go1.13, context.Context requires Type assertions. Don't implement the context.Context method. Compared with method 1, the implementation only requires WithContext once.
It is recommended to use method 1, which is clear and simple, and method 2 is complicated and not fully tested.
The example import package uses gopath, and the implementation of this feature also reflects the advantage of echo.Context as an interface.
package main
import (
"context"
"fmt"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"net/http"
)
func main() {
// Echo instance
e := echo.New()
// Middleware
e.Use(NewMiddlewareContextValue)
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Routes
e.GET("/", hello)
e.GET("/val", getval)
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
// Handler
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
func getval(c echo.Context) error {
c.Set("111", "aa")
c.Set("222", "bb")
return c.String(http.StatusOK, fmt.Sprint(c.Request().Context()))
}
// ---------- method1 ----------
func NewMiddlewareContextValue(fn echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
return fn(contextValue{ctx})
}
}
type contextValue struct {
echo.Context
}
// Get retrieves data from the context.
func (ctx contextValue) Get(key string) interface{} {
// get old context value
val := ctx.Context.Get(key)
if val != nil {
return val
}
return ctx.Request().Context().Value(key)
}
// Set saves data in the context.
func (ctx contextValue) Set(key string, val interface{}) {
ctx.SetRequest(ctx.Request().WithContext(context.WithValue(ctx.Request().Context(), key, val)))
}
// ---------- method2 ----------
func NewMiddlewareContextValue2(fn echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
ctxdata := contextValueData2{
Context: ctx.Request().Context(),
}
ctx.SetRequest(ctx.Request().WithContext(ctxdata))
return fn(&contextValue2{Context: ctx, contextValueData2: ctxdata})
}
}
type contextValue2 struct {
echo.Context
contextValueData2
}
type contextValueData2 struct {
context.Context
Data map[string]interface{}
}
// Get retrieves data from the context.
func (ctx *contextValue2) Get(key string) interface{} {
// get old context value
val := ctx.Context.Get(key)
if val != nil {
return val
}
// get my data value
val, ok := ctx.contextValueData2.Data[key]
if ok {
return val
}
return ctx.contextValueData2.Context.Value(key)
}
// Set saves data in the context.
func (ctx *contextValue2) Set(key string, val interface{}) {
if ctx.Data == nil {
ctx.contextValueData2.Data = make(map[string]interface{})
}
ctx.contextValueData2.Data[key] = val
}
func (ctx contextValueData2) Value(key interface{}) interface{} {
str, ok := key.(string)
if ok {
val, ok := ctx.Data[str]
if ok {
return val
}
}
return ctx.Context.Value(key)
}