英文:
Does Set() method of echo.Context saves the value to the underlying context.Context?
问题
我正在使用 Echo 框架,并且想要在设置一些自定义值后,将 Go 的内置 context.Context
传递给 underlying echo.Context。
为了实现这一点,我认为我可以先应用 echo.Context
的 Set(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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论