英文:
Request context set in negroni middleware is lost in nested gorilla Subrouter
问题
我的基本main
设置:
muxRouter := mux.NewRouter()
v1Router.Router(muxRouter.PathPrefix("/v1").Subrouter())
http.Handle("/", muxRouter)
n := negroni.Classic()
n.Use(negroni.HandlerFunc(apiRouter.Middleware))
n.UseHandler(muxRouter)
s := &http.Server{
Addr: ":6060",
Handler: n,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
在apiRouter.Middleware
中,我设置了以下上下文:
context.Set(req, helperKeys.DomainName, "some-value")
然而,在v1Router.Router
中的某个handlerFunc
中,当尝试Get
上下文的值时,结果为nil
:
domain := context.Get(req, helperKeys.DomainName)
fmt.Println("DomainName", domain)
输出:DomainName <nil>
我知道Set
方法是正确的,因为在apiRouter.Middleware
中设置后立即获取值将返回正确的字符串值。
英文:
My basic main
setup:
muxRouter := mux.NewRouter()
v1Router.Router(muxRouter.PathPrefix("/v1").Subrouter())
http.Handle("/", muxRouter)
n := negroni.Classic()
n.Use(negroni.HandlerFunc(apiRouter.Middleware))
n.UseHandler(muxRouter)
s := &http.Server{
Addr: ":6060",
Handler: n,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
Inside the apiRouter.Middleware
I have set the following context:
context.Set(req, helperKeys.DomainName, "some-value")
However, in some handlerFunc within v1Router.Router
when trying to Get
the context's value, the result is nil:
domain := context.Get(req, helperKeys.DomainName)
fmt.Println("DomainName", domain)
Prints: DomainName <nil>
I know that the Set
method is correct as getting the value immediately after setting it in the apiRouter.Middleware
will return the correct string value.
答案1
得分: 1
我最终使用了Go 1.7
内置的Context
:
context.Set(req, helperKeys.DomainName, "some-value")
// 替换为:
ctx := req.Context()
ctx = context.WithValue(ctx, helperKeys.DomainName, "some-value")
req = req.WithContext(ctx)
并且
domain := context.Get(req, helperKeys.DomainName)
// 替换为:
domain := req.Context().Value(helperKeys.DomainName).(string)
英文:
I ended up using Go 1.7
's built in Context
:
context.Set(req, helperKeys.DomainName, "some-value")
// Replaced with:
ctx := req.Context()
ctx = context.WithValue(ctx, helperKeys.DomainName, "some-value")
req = req.WithContext(ctx)
AND
domain := context.Get(req, helperKeys.DomainName)
// Replaced with:
domain := req.Context().Value(helperKeys.DomainName).(string)
答案2
得分: 0
根据你的回答,看起来你正在尝试在上下文中存储一个数据库。我不建议这样做。相反,你可以尝试像这样做:
type Something struct {
DB *sql.DB // 或其他数据库对象
}
func (s *Something) CreateUser(w http.ResponseWriter, r *http.Request) {
// 使用 s.DB 来访问数据库
fmt.Fprintln(w, "创建了一个用户...")
}
func main() {
db := ...
s := Something{db}
http.HandleFunc("/", s.CreateUser)
// ... 其他部分与正常情况下相似
}
这样可以让处理程序在不必每次都设置上下文的情况下访问数据库。上下文值应该保留给那些在运行时无法设置的事物。例如,特定于该网络请求的请求ID。那些超出请求生命周期的事物通常不属于这个范畴,而你的数据库连接将超出请求的生命周期。
如果你确实需要上下文值,你应该:
- 使用具有类型的getter和setter
- “包应该将键定义为未导出类型,以避免冲突。”- 来自Go源代码
下面是一个示例,我在这篇博文中更详细地讨论了上下文值的一般情况:
type userCtxKeyType string
const userCtxKey userCtxKeyType = "user"
func WithUser(ctx context.Context, user *User) context.Context {
return context.WithValue(ctx, userCtxKey, user)
}
func GetUser(ctx context.Context) *User {
user, ok := ctx.Value(userCtxKey).(*User)
if !ok {
// 记录此问题
return nil
}
return user
}
希望对你有所帮助!
英文:
Based on your answer, it looks like you are trying to store a database in the context. I wouldn't suggest doing that. Instead try something like this:
type Something struct {
DB *sql.DB // or some other DB object
}
func (s *Something) CreateUser(w http.ResponseWriter, r *http.Request) {
// use s.DB to access the database
fmt.Fprintln(w, "Created a user...")
}
func main() {
db := ...
s := Something{db}
http.HandleFunc("/", s.CreateUser)
// ... everything else is pretty much like normal.
}
This gives your handlers access to the database while not having to set it on the context every single time. Context values should be reserved for things that you can't possibly set until runtime. For example, a request ID that is specific to that web request. Things that outlive the request don't typically fall into this category, and your DB connection will outlive the request.
If you do actually need context values, you should:
- Use getters and setters that are typed
- "packages should define keys as an unexported type to avoid collisions." - From the Go source code
An example of this is shown below, and I talk more about context values in general in this blog post:
type userCtxKeyType string
const userCtxKey userCtxKeyType = "user"
func WithUser(ctx context.Context, user *User) context.Context {
return context.WithValue(ctx, userCtxKey, user)
}
func GetUser(ctx context.Context) *User {
user, ok := ctx.Value(userCtxKey).(*User)
if !ok {
// Log this issue
return nil
}
return user
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论