首页 > 解决方案 > 使用 http.NewRequestWithContext() 创建的请求在传递给中间件时会丢失上下文

问题描述

在下面的程序中,我有两个路由器。一个在公共接入点工作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
}

标签: restgogo-chi

解决方案


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

上下文(在某种程度上简化并参考NewRequestWithContextAPI)只是一个内存中的对象,您可以使用它来控制请求的生命周期(处理/触发取消)。

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

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

如果您仍想从上下文发送附加值,那么您将不得不使用其他属性,如 HTTP 标头、参数、正文来发送所需的信息。这可以提供有关如何通过 HTTP 从上下文序列化数据的更多信息。


推荐阅读