`echo.Context`的`Set()`方法是否将值保存到底层的`context.Context`中?

huangapple go评论89阅读模式
英文:

Does Set() method of echo.Context saves the value to the underlying context.Context?

问题

我正在使用 Echo 框架,并且想要在设置一些自定义值后,将 Go 的内置 context.Context 传递给 underlying echo.Context

为了实现这一点,我认为我可以先应用 echo.ContextSet(key string, val interface{}) 方法,然后提取底层的 context.Context

问题是,这种方式可行吗?换句话说,echo.Context.Set(...) 是否像 WithValue 一样直接在 context.Context 上设置值?还是我需要额外的步骤来复制我的自定义条目。

附注:我不想将 echo.Context 传递给我的应用程序的更深层次,这就是为什么我不想直接使用它,而是获取引用的 context.Context

英文:

I am using Echo framework and want to pass the Go's built-in context.Context underlying echo.Context after setting some custom values.

To achieve it, I think I could first apply Set(key string, val interface{}) method of echo.Context and then extract the underlying context.Context.

Question is is it possible to do it this way? In other words, does echo.Context.Set(...) sets the value directly on the context.Context just like WithValue does? Or should I take extra steps to copy my custom entries down.

P.S. I do not want to pass echo.Context to deeper layers of my app, that's why I do not want to directly use it but get the referring context.Context

答案1

得分: 7

方法1:重新实现echo.Context.Get和echo.Context.Set方法来操作ctx.Request().Context()对象。

缺点:每次调用Set方法都会调用http.Request.WithContext,并且*http.Request会被复制一次。详细实现请参考WithContext方法的实现。

方法2:重新实现echo.Context.Get和echo.Context.Set方法来操作contextValueData2对象,并将http.Request.WithContext设置为自定义的context.Context contextValueData2。

缺点:在go1.13之前,context.Context需要进行类型断言。不要实现context.Context方法。与方法1相比,该实现只需要一次WithContext。

建议使用方法1,清晰简单,方法2复杂且未经完全测试。

示例导入包使用gopath,并且该功能的实现也反映了echo.Context作为接口的优势。

package main

import (
	"context"
	"fmt"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"net/http"
)

func main() {
	// Echo实例
	e := echo.New()

	// 中间件
	e.Use(NewMiddlewareContextValue)
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// 路由
	e.GET("/", hello)
	e.GET("/val", getval)

	// 启动服务器
	e.Logger.Fatal(e.Start(":1323"))
}

// 处理程序
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()))
}

// ---------- 方法1 ----------
func NewMiddlewareContextValue(fn echo.HandlerFunc) echo.HandlerFunc {
	return func(ctx echo.Context) error {
		return fn(contextValue{ctx})
	}
}

type contextValue struct {
	echo.Context
}

// Get从上下文中检索数据。
func (ctx contextValue) Get(key string) interface{} {
	// 获取旧的上下文值
	val := ctx.Context.Get(key)
	if val != nil {
		return val
	}
	return ctx.Request().Context().Value(key)
}

// Set将数据保存在上下文中。
func (ctx contextValue) Set(key string, val interface{}) {
	ctx.SetRequest(ctx.Request().WithContext(context.WithValue(ctx.Request().Context(), key, val)))
}

// ---------- 方法2 ----------

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从上下文中检索数据。
func (ctx *contextValue2) Get(key string) interface{} {
	// 获取旧的上下文值
	val := ctx.Context.Get(key)
	if val != nil {
		return val
	}
	// 获取我的数据值
	val, ok := ctx.contextValueData2.Data[key]
	if ok {
		return val
	}
	return ctx.contextValueData2.Context.Value(key)
}

// Set将数据保存在上下文中。
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)
}
英文:

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)
}

huangapple
  • 本文由 发表于 2021年9月25日 20:25:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/69326129.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定