基本介绍

请求流程往往会在上下文中共享一些自定义设置的变量,例如在请求开始之前通过中间件设置一些变量,随后在路由服务方法中可以获取该变量并相应对一些处理。这种需求非常常见。在GoFrame框架中,我们推荐使用Context上下文对象来处理流程共享的上下文变量,甚至将该对象进一步传递到依赖的各个模块方法中。该Context对象类型实现了标准库的context.Context接口,该接口往往会作为模块间调用方法的第一个参数,该接口参数也是Golang官方推荐的在模块间传递上下文变量的推荐方式。

方法列表:

func (r *Request) GetCtx() context.Context
func (r *Request) SetCtx(ctx context.Context)
func (r *Request) GetCtxVar(key interface{}, def ...interface{}) *gvar.Var
func (r *Request) SetCtxVar(key interface{}, value interface{})

简要说明:

  1. GetCtx方法用于获取当前的context.Context对象,作用同Context方法。
  2. SetCtx方法用于设置自定义的context.Context上下文对象。
  3. GetCtxVar方法用于获取上下文变量,并可给定当该变量不存在时的默认值。
  4. SetCtxVar方法用于设置上下文变量。

使用示例

示例1,SetCtxVar/GetCtxVar

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

const (
	TraceIdName = "trace-id"
)

func main() {
	s := g.Server()
	s.Group("/", func(group *ghttp.RouterGroup) {
		group.Middleware(func(r *ghttp.Request) {
			r.SetCtxVar(TraceIdName, "HBm876TFCde435Tgf")
			r.Middleware.Next()
		})
		group.ALL("/", func(r *ghttp.Request) {
			r.Response.Write(r.GetCtxVar(TraceIdName))
		})
	})
	s.SetPort(8199)
	s.Run()
}

可以看到,我们可以通过SetCtxVarGetCtxVar来设置和获取自定义的变量,该变量生命周期仅限于当前请求流程。

执行后,访问 http://127.0.0.1:8199/ ,页面输出内容为:

HBm876TFCde435Tgf

示例2,SetCtx

SetCtx方法常用于中间件中整合一些第三方的组件,例如第三方的链路跟踪组件等等。

为简化示例,这里我们将上面的例子通过SetCtx方法来改造一下来做演示。

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

const (
	TraceIdName = "trace-id"
)

func main() {
	s := g.Server()
	s.Group("/", func(group *ghttp.RouterGroup) {
		group.Middleware(func(r *ghttp.Request) {
			ctx := context.WithValue(r.Context(), TraceIdName, "HBm876TFCde435Tgf")
			r.SetCtx(ctx)
			r.Middleware.Next()
		})
		group.ALL("/", func(r *ghttp.Request) {
			r.Response.Write(r.Context().Value(TraceIdName))
			// 也可以使用
			// r.Response.Write(r.GetCtxVar(TraceIdName))
		})
	})
	s.SetPort(8199)
	s.Run()
}

执行后,访问 http://127.0.0.1:8199/ ,页面输出内容为:

HBm876TFCde435Tgf



Content Menu

  • No labels

8 Comments

  1. 示例2,SetCtx 

    这个方法是在哪个版本加的? 我用 v1.14.6 报错

    另外问一下,用示例1 的 SetCtxVar  取值时可以用r.Context().Value(TraceIdName)吗?

    这个编辑器挺有意思,复制的文字会带上颜色格式。

    可以用工具栏清除格式


    1. 示例2,SetCtx 

      这个方法是在哪个版本加的? 我用 v1.14.6 报错

      你好,这个是最新版本的新功能,目前在master分支上。该空间的文档是最新版本的,旧版本的文档请查看空间快捷连接:

      另外问一下,用示例1 的 SetCtxVar  取值时可以用 r.Context().Value(TraceIdName) 吗?

      支持啊,Value方法是标准库context的方法。

      sanrentai 


  2. 有个东西反馈一下,不知道是不是bug

    调用 Context() 会导致 ctx 会变

    先贴代码


        r := g.RequestFromCtx(ctx)
        r.SetCtxVar("token", "aaaaaaaaaaaaaa")
        fmt.Printf("1、%v\n", ctx)
        fmt.Printf("2、%t\n", ctx)
        fmt.Printf("3、%T\n", ctx)
        fmt.Printf("4、%p\n", ctx)
        ctx = r.Context()
        fmt.Printf("1、%v\n", ctx)
        fmt.Printf("2、%t\n", ctx)
        fmt.Printf("3、%T\n", ctx)
      fmt.Printf("4、%p\n", ctx)

    打印出来的结果是下面

    1、context.Background.WithValue(type trace.traceContextKeyType, val <not Stringer>).WithValue(type string, val <not Stringer>).WithValue(type gctx.StrKey, val <not Stringer>).WithValue(type trace.traceContextKeyType, val <not Stringer>)
    2、&{%!t(*context.valueCtx=&{0xc0003b2ae0 MiddlewareServerTracingHandled 1}) %!t(trace.traceContextKeyType=0) %!t(*trace.recordingSpan=&{{0 0} {[240 89 112 9 107 214 31 23 5 184 251 47 100 154 67 151] [49 4 156 121 115 9 55 194] 1 {[]} false} 2 /user/info {13892617968244255516 5663679801 0x10ebd80} {0 0 <nil>} {0 } 0 {[240 89 112 9 107 214 31 23 5 184 251 47 100 154 67 151] [217 5 156 121 160 170 13 178] 1 {[]} false} [{hostname {4 0 DESKTOP-005IF25-wsl <nil>}} {ip.intranet {4 0 172.21.5.116,172.17.0.1 <nil>}} {host.name {4 0 DESKTOP-005IF25-wsl <nil>}}] 0 {[] 128 0} {[] 128 0} <nil> 0xc000356200})}
    3、*context.valueCtx
    4、0xc0002fa780
    1、context.Background.WithValue(type trace.traceContextKeyType, val <not Stringer>).WithValue(type string, val <not Stringer>).WithValue(type gctx.StrKey, val <not Stringer>).WithValue(type trace.traceContextKeyType, val <not Stringer>).WithValue(type string, val aaaaaaaaaaaaaa)
    2、&{%!t(*context.valueCtx=&{0xc0003b2d80 0 0xc000490480}) %!t(string=token) %!t(string=aaaaaaaaaaaaaa)}
    3、*context.valueCtx
    4、0xc0003b30e0

    在上面 SetCtxVar 后,ctx 并没有 token=aaaaaaaaaa 的值

    r.Context() 后,ctx 就有了

    应该是SetCtxVar 后,r 的 context 是重新设了,保存在 r.context

    这时候 r.context 和ctx 是不一样的。token 保存在 r.context 里


    应该是在 中间件调用 controller 方法的时候 r.context 是空的,但是传过来的 ctx 是绑定了 r 的

    所以就产生了这种现象



    1. 简而言之,ctx的传递都是值传递

  3. gcf

    controller中的ctx如何获取

    r *ghttp.Request

    对象呢?

  4. g.RequestFromCtx(ctx) 这个就是返回的 *ghttp.Request

  5. 请求流程往往会在上下文中共享一些自定义设置的变量,例如在请求开始之前通过中间件设置一些变量,随后在路由服务方法中可以获取该变量并相应对一些处理。

    中的“并相应对一些处理”

    应修改成:

    “并相应做一些处理”。