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

Posted

技术标签:

【中文标题】echo.Context 的 Set() 方法是不是将值保存到底层 context.Context?【英文标题】:Does Set() method of echo.Context saves the value to the underlying context.Context?echo.Context 的 Set() 方法是否将值保存到底层 context.Context? 【发布时间】:2021-11-18 09:27:46 【问题描述】:

我正在使用Echo framework,并希望在设置一些自定义值后通过Go的内置context.Contextunderlying echo.Context。

为了实现它,我想我可以先应用echo.ContextSet(key string, val interface)方法,然后提取底层的context.Context

问题是可以这样做吗?换句话说,echo.Context.Set(...) 是否像WithValue 一样直接在context.Context 上设置值?或者我应该采取额外的步骤来复制我的自定义条目。

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

【问题讨论】:

【参考方案1】:

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

缺点:每个Set方法都会调用一次http.Request.WithContext,复制一次*http.Request。详见WithContext方法的实现。

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

缺点:在go1.13之前,context.Context需要Type断言。不要实现 context.Context 方法。与方法一相比,实现只需要一次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 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(contextValuectx)
    


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(&contextValue2Context: 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)

【讨论】:

以上是关于echo.Context 的 Set() 方法是不是将值保存到底层 context.Context?的主要内容,如果未能解决你的问题,请参考以下文章

java set重复值是不存还是覆盖

一道java面试题,判断Set里的元素是不是重复

Python set([]) 如何检查两个对象是不是相等?一个对象需要定义哪些方法来自定义它?

C++ 中是不是有类似于 ruby​​ 的 set_trace_function 的工具?

如何检查 unordered_set 是不是重叠?

检查表是不是已在代码优先方法中创建