使用 http.NewRequestWithContext() 创建的请求在传递给中间件时会丢失上下文

Request created with http.NewRequestWithContext() looses context when passed to middleware

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

问题

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

错误处理之类的东西没有写到这里 post 一大堆代码。

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
}

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

上下文(在一定程度上简化并参考 NewRequestWithContext API),只是一个 in-memory object,您可以使用它来控制 lifetime of the request(Handling/Triggering 次取消)。

但是您的代码正在进行 HTTP 调用,该调用使用 HTTP 协议通过线路(编组)。该协议不了解 golang 的上下文或其值。 在您的场景中,/tasks/str/tasks/solution 都在同一台服务器上 运行。如果他们在不同的服务器上,可能还有不同的语言和应用程序服务器,那么上下文就无法发送了。

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

如果您仍想从上下文发送额外的值,那么您将不得不使用其他属性,如 HTTP Headers、Params、Body 来发送所需的信息。 This can provide more info 关于如何通过 HTTP 从上下文序列化数据。