使用 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 从上下文序列化数据。
在下面的程序中我有两个路由器。一个在 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 从上下文序列化数据。