问题陈述
我想将HTTP请求的生存期绑定到web应用程序范围外创建的上下文。因此,我编写了以下中间件(使用github.com/go-chi/chi):
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(c))
})
}
}中间件用于以下最小测试用例:
package main
import (
"context"
"net/http"
"github.com/SentimensRG/ctx"
"github.com/SentimensRG/ctx/sigctx"
"github.com/go-chi/chi"
)
func greet(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(c))
})
}
}
func main() {
r := chi.NewMux()
r.Use(BindContext(ctx.AsContext(sigctx.New())))
r.Get("/", greet)
http.ListenAndServe(":8080", r)
}处理程序会出现以下错误:
2018/07/25 14:58:57 http: panic serving [::1]:56527: interface conversion: interface {} is nil, not *chi.Context
goroutine 35 [running]:
net/http.(*conn).serve.func1(0xc42014a0a0)
/usr/local/go/src/net/http/server.go:1726 +0xd0
panic(0x12749c0, 0xc42014c200)
/usr/local/go/src/runtime/panic.go:502 +0x229
github.com/go-chi/chi.(*Mux).routeHTTP(0xc4201180c0, 0x12fcf00, 0xc420166000, 0xc420160200)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:400 +0x2f3
github.com/go-chi/chi.(*Mux).(github.com/go-chi/chi.routeHTTP)-fm(0x12fcf00, 0xc420166000, 0xc420160200)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:368 +0x48
net/http.HandlerFunc.ServeHTTP(0xc420142010, 0x12fcf00, 0xc420166000, 0xc420160200)
/usr/local/go/src/net/http/server.go:1947 +0x44
main.fail.func1.1(0x12fcf00, 0xc420166000, 0xc420160100)
/Users/lthibault/go/src/github.com/lthibault/mesh/cmd/scratch/main.go:22 +0x77
net/http.HandlerFunc.ServeHTTP(0xc420148000, 0x12fcf00, 0xc420166000, 0xc420160100)
/usr/local/go/src/net/http/server.go:1947 +0x44
github.com/go-chi/chi.(*Mux).ServeHTTP(0xc4201180c0, 0x12fcf00, 0xc420166000, 0xc420160000)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:81 +0x221
net/http.serverHandler.ServeHTTP(0xc420150000, 0x12fcf00, 0xc420166000, 0xc420160000)
/usr/local/go/src/net/http/server.go:2694 +0xbc
net/http.(*conn).serve(0xc42014a0a0, 0x12fd1c0, 0xc42014c080)
/usr/local/go/src/net/http/server.go:1830 +0x651
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:2795 +0x27b不雅解
问题似乎来自于Mux.routeHTTP,在那里尝试从r.Context()中恢复*chi.Context。r.WithContext似乎不会将存储在请求上下文中的值传递给新上下文。
显而易见(尽管很难看)的解决办法是:
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rctx := r.Context().Value(chi.RouteCtxKey).(*chi.Context)
c = context.WithValue(c, chi.RouteCtxKey, rctx)
h.ServeHTTP(w, r.WithContext(c))
})
}
}这是可行的,但让我感到不安。我真的需要手动将每个相关值从r.Context()传递到传递给r.WithContext()的上下文中吗?
有几个失败案例,在这里:
(一句话:没什么好的!)
我的问题如下
是否有标准的“合并”--传递给r.WithContext的上下文与r.Context中的退出上下文
发布于 2018-07-26 13:03:44
似乎没有开箱即用的解决方案,但github.com/SentimensRG/ctx提供了专门用于此目的的mergectx子包。
解决方案是使用mergectx.Merge。
发布于 2018-07-25 16:35:28
您不应该将传入请求的上下文替换为不相关的上下文。首先:
包上下文定义上下文类型,它包含跨API边界和进程之间的截止日期、取消信号和其他请求范围的值。
在发生任何请求之前都会调用sigctx.New(),因此根据定义,它不是请求作用域。几乎所有的代码都期望在以下情况下请求上下文被取消:( a)请求完成;或( b)客户端中止请求(通常是因为它对响应不再感兴趣)。你通过替换上下文来打破这一假设。您还删除了其他中间件先前添加到上下文中的任何值。
似乎您希望中止SIGINT或SIGTERM上的请求。应该将取消条件添加到请求上下文中,而不是完全替换它。也许是这样的:
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rCtx := r.Context()
ctx, cancel := context.WithCancel(rCtx)
go func() {
select {
case <-c.Done(): // SIGINT/SIGTERM
case <-rCtx.Done(): // Request finished or client aborted
}
cancel()
}()
h.ServeHTTP(w, r.WithContext(ctx))
})
}
}更新:
要让用户配置上下文,请接受从请求上下文派生新上下文的函数(尽管用户还可以提供直接这样做的中间件):
func WithContext(new func(context.Context) context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(new(r.Context()))
h.ServeHTTP(w, r)
})
}
}发布于 2019-02-26 11:02:02
我也面临着同样的问题,并且能够通过使用chi.NewRouteContext创建新的上下文来解决这个问题。
请求是使用httptest提出的。您可以使用r.WithContext更新请求的上下文。
示例
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("key", "value")
r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rctx))
handler := func(w http.ResponseWriter, r *http.Request) {
key := chi.URLParam(r, "key") // "value"
}
handler(w, r)参见aapolkovsky的折叠要点:https://gist.github.com/aapolkovsky/1375348cab941e36c62da24a32fbebe7
https://stackoverflow.com/questions/51521279
复制相似问题