【问题标题】:Request created with http.NewRequestWithContext() looses context when passed to middleware使用 http.NewRequestWithContext() 创建的请求在传递给中间件时会丢失上下文
【发布时间】:2021-11-04 19:45:55
【问题描述】:

在下面的程序中,我有两个路由器。一个在localhost:3000 工作,就像一个公共接入点。它还可以将带有数据的请求发送到另一个本地地址localhost:8000,在该地址正在处理数据。第二个路由器在localhost:8000 工作并处理第一个路由器的处理请求。

问题

第一个路由器使用http.NewRequestWithContext() 函数向第二个路由器发送一个带有上下文的请求。该值被添加到上下文中,并且上下文被添加到请求中。当请求到达第二个路由器时,它没有先前添加的值。

错误处理之类的一些东西没有写到这里不贴代码墙。

package main
import (
    "bytes"
    "context"
    "net/http"
    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
)

func main() {
    go func() {
        err := http.ListenAndServe(
            "localhost:3000",
            GetDataAndSolve(),
        )
        if err != nil {
            panic(err)
        }
    }()

    go func() {
        err := http.ListenAndServe( // in GetDataAndSolve() we send requests
            "localhost:8000", // with data for processing
            InternalService(),
        )
        if err != nil {
            panic(err)
        }
    }()

    // interrupt := make(chan os.Signal, 1) 
    // signal.Notify(interrupt, syscall.SIGTERM, syscall.SIGINT)
    // <-interrupt // just a cool way to close the program, uncomment if you need it
}
func GetDataAndSolve() http.Handler {
    r := chi.NewRouter()
    r.Use(middleware.Logger)

    r.Get("/tasks/str", func(rw http.ResponseWriter, r *http.Request) {
        // receiving data for processing...
        taskCtx := context.WithValue(r.Context(), "str", "strVar") // the value is being
        postReq, err := http.NewRequestWithContext(                // stored to context
            taskCtx, // context is being given to request
            "POST",
            "http://localhost:8000/tasks/solution",
            bytes.NewBuffer([]byte("something")),
        )
        postReq.Header.Set("Content-Type", "application/json") // specifying for endpoint
        if err != nil {                                        // what we are sending
            return
        }

        resp, err := http.DefaultClient.Do(postReq) // running actual request
        // pls, proceed to Solver()

        // do stuff to resp
        // also despite arriving to middleware without right context
        // here resp contains a request with correct context
    })

    return r
}
func Solver(next http.Handler) http.Handler { // here we end up after sending postReq
    return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
        if r.Context().Value("str").(string) == "" {
            return // the request arrive without "str" in its context
        }

        ctxWithResult := context.WithValue(r.Context(), "result", mockFunc(r.Context()))
        next.ServeHTTP(rw, r.Clone(ctxWithResult))
    })
}

func InternalService() http.Handler {
    r := chi.NewRouter()
    r.Use(middleware.Logger)

    r.With(Solver).Post("/tasks/solution", emptyHandlerFunc)

    return r
}

【问题讨论】:

  • 客户端请求中的上下文不是用于向服务器发送数据,而是用于取消。你不能用上下文做你想做的事。要将数据发送到其他服务器,请使用 URL 查询参数、标头或正文。不是上下文,那是行不通的,不管你多么希望它会。
  • Context - "对于传出的客户端请求,上下文控制取消。" / WithContext - "对于传出的客户端请求,上下文控制整个请求及其响应的生命周期:获取连接、发送请求、读取响应头和响应体。"

标签: rest go go-chi


【解决方案1】:

你对上下文的理解不正确。

上下文(在一定程度上简化并参考NewRequestWithContext API)只是一个内存中的对象,您可以使用它来控制lifetime of the request(处理/触发取消)。

但是,您的代码正在进行 HTTP 调用,该调用使用 HTTP 协议通过线路(编组)。该协议不理解 golang 的上下文或其值。 在您的场景中,/tasks/str/tasks/solution 都在同一台服务器上运行。如果它们位于不同的服务器上怎么办,也可能是不同的语言和应用程序服务器,所以上下文不能被发送。

由于 API 位于同一服务器中,也许您可​​以避免进行完整的 HTTP 调用,而直接调用 API/方法。它也可能会更快。

如果您仍想从上下文发送附加值,那么您将不得不利用其他属性(如 HTTP 标头、参数、正文)来发送所需的信息。 This can provide more info 关于如何通过 HTTP 从上下文序列化数据。

【讨论】:

  • 谢谢。我有一个附带问题。您说上下文用于控制请求的生命周期,因此使用了 WithCancel 或 WithDeadline。那么当涉及到 http 请求时,WithValue 是用来做什么的呢?
  • @mcv_dev 上下文的withValue 用于将值传递给该上下文或其子上下文将被传递到的任何子调用。对于 HTTP 请求,它不会发挥任何作用(除非 HTTP 库使用此值,在这种情况下它不是,所以它没有用)
  • go.dev/blog/context 的示例使用 WithValue 传递范围为请求的数据:“处理请求的 goroutines 集通常需要访问特定于请求的值,例如最终用户、授权令牌和请求的截止日期。”在 HTTP 设置中使用 WithValue 非常有效 - 但就像答案所说,上下文仅适用于处理 http 请求那一侧的代码。客户端请求和服务器请求都有一个上下文,但它们绝不是相同的上下文;值的任何相似性纯属偶然。
猜你喜欢
  • 2021-07-14
  • 2017-02-18
  • 2018-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-27
  • 2012-11-28
相关资源
最近更新 更多