乐趣区

关于go:读源码为什么注册路由时没有传入上下文在接口方法中却能取到

作为一个工作 8 年的老程序员通知你:浏览源码和查看官网文档,是解决问题最高效的方法。不信你来看,这个困扰了读者半天的问题我查了源码和文档后霎时解决。

前言

上周五有位读者私信我一个问题,说困扰了他半天,钻研了一个上午也没搞明确。

是一位运维转 Go 的敌人,最近有不少运维、测试、甚至 Java、PHP 转 Go 的敌人加我。

这个问题并不是喋喋不休就能讲清楚的,我就整顿了一篇文章给他。

高兴

正如上图所示,反馈特地的好,更文的高兴又找到了,我把这个问题和解答又好好整顿了一下,分享给大家,心愿对大家有帮忙.

也分享一下我作为一个工作 8 年的老程,解决问题的心得:

浏览源码和查看官网文档,是解决问题最高效的方法。

发问

在 gofame 框架的 demo 案例中,如下图所示:

  1. 为什么左侧路由绑定这里没有向 controller 中传入 context 的值,在 controller 中却能取到值?
  2. 如何赋值和接管 context?

先说论断

  1. 对于 ctx context.Context 上下文,Server 组件会主动从申请中获取并传递给接口办法,申明并初始化了 context 初始值为context.Background()
  2. 能够通过 GetCtx、SetCtx、GetCtxVar、SetCtxVar 这些办法轻松的为 context 赋值和取值
  3. 通过示例代码轻松可知:咱们能够通过 ghttp.Request 实例轻松的操作 context。

解答

问题 1. 为什么左侧路由绑定这里没有向 controller 中传入 context 的值,在 controller 中却能取到值?

先说论断:对于 ctx context.Context 上下文,Server 组件会主动从申请中获取并传递给接口办法。

要害代码是上图中的s := g.Server():

追踪一下它的源码能够发现:申明并初始化了 ctx 的初始值:context.Background()

再带大家看一下 context.Background() 的源码和正文。

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {return background}

咱们能够发现这里返回了一个non-nil, empty Context

问题 2. 如何赋值和接管 context?

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

简要阐明:

  • GetCtx 办法用于获取以后的 context.Context 对象,作用同 Context 办法。
  • SetCtx 办法用于设置自定义的 context.Context 上下文对象。
  • GetCtxVar 办法用于获取上下文变量,并可给定当该变量不存在时的默认值。
  • 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) {
      // 向 context 中赋值
            r.SetCtxVar(TraceIdName, "1234567890abcd")
            r.Middleware.Next()})
        group.ALL("/", func(r *ghttp.Request) {
      // 从 context 中取值
            r.Response.Write(r.GetCtxVar(TraceIdName))
        })
    })
    s.SetPort(8199)
    s.Run()}

能够看到:咱们能够通过 SetCtxVar 和 GetCtxVar 来设置和获取自定义的变量,该变量生命周期仅限于以后申请流程。

执行后,拜访 http://127.0.0.1:8199/,页面输入内容为:1234567890abcd

示例 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, "1234567890abcd")
            r.SetCtx(ctx)
            r.Middleware.Next()})
        group.ALL("/", func(r *ghttp.Request) {
      // 看到这里的示例代码,更能解答问题 1,通过 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/,页面输入内容为:1234567890abcd

总结

通过下面的示例,咱们能更好的了解这位星友提出的困惑:

  1. 对于 ctx context.Context 上下文,Server 组件会主动从申请中获取并传递给接口办法,申明并初始化了 context 初始值为context.Background()
  2. 能够通过 GetCtx、SetCtx、GetCtxVar、SetCtxVar 这些办法轻松的为 context 赋值和取值
  3. 通过示例代码轻松可知:咱们能够通过 ghttp.Request 实例轻松的操作 context。

参考链接

  • 路由注册 - 标准路由
  • 申请输出 -Context

一起提高

如果你感觉这篇文章不错,欢送点赞,评论。

我的思否声望什么时候能到 200 呀!?

退出移动版