英文:
How can I use the new context package from Go like I would use gorilla context library?
问题
Go语言引入了一个名为"context"的新包,并且在最近的版本中(我认为是Go 1.7)我们应该能够像使用gorilla/context包一样使用它:
http://www.gorillatoolkit.org/pkg/context
使用gorilla context,您可以非常轻松地设置和获取与请求、处理程序和中间件相关的变量。
在gorilla context中设置一个值非常简单:
func handleFunc(w http.ResponseWriter, r *http.Request) {
context.Set(r, "foo", "bar")
}
获取值的方法如下:
func handleFunc(w http.ResponseWriter, r *http.Request) {
val := context.Get(r, "foo")
}
我了解到我们可以在中间件中使用它,以便下一个中间件可以使用在前一个中间件中设置的变量。我希望能够在Go的context包中实现这一点。
我知道获取值很简单,就像这样:
func handleFunc(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Context().Value("foo"))
}
但是我不知道如何设置这个值。对我来说,这不是很直观,我真的不明白该怎么做。
英文:
Go has come with a new package called context and with recent versions (Go 1.7 I think) we should be able to use it in the same way as gorilla/context package:
http://www.gorillatoolkit.org/pkg/context
With gorilla context you can very easily set and get variables that are relevant to a request, it's handlers, and middlewares.
To set a value in gorilla context is really easy:
func handleFunc(w http.ResponseWriter, r *http.Request) {
context.Set(r, "foo", "bar")
}
To get the value we can do:
func handleFunc(w http.ResponseWriter, r *http.Request) {
val := context.Get(r, "foo")
}
I understand that we can use this in middlewares so that the next middleware can use variables that were set in previous middleware. I would like to be able to do this with Go context package.
I understand that to get a value is quite simple like this:
func handleFunc(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Context().Value("foo"))
}
But I have no idea how to set the value. It was not very intuitive for me and I don't really understand how to do it.
答案1
得分: 3
请注意,我将为您翻译以下内容:
查看“探索上下文包”,使用WithValue
和与请求相关联的上下文:
中间件
在Go中,中间件是指包装在多路复用器周围的HTTP处理程序。有几个第三方中间件解决方案(如negroni),但实际上标准库支持非常类似的模式。在请求中使用上下文允许我们在请求中保存数据。
func putClientIPIntoContext(r *http.Request) context.Context {
ci := r.RemoteAddr
fwd := r.Header.Get("X-Forwarded-For")
if fwd != "" {
ci = fwd
}
ctx := context.WithValue(r.Context(), ClientIPKey, ci)
return ctx
}
上下文可以存储请求范围的变量。这在编写“中间件”时非常有用,但它有点“反模式”——有点神奇,因为它不是类型安全的。
详细了解“在Go中避免或减轻上下文值的陷阱”。
下面的示例仅显示了如何使用上面的身份验证逻辑来验证在访问任何具有路径前缀“/dashboard/”的页面时用户是否已登录。类似的方法可以用于在允许用户访问任何具有路径前缀“/admin/”的页面之前验证用户是否为管理员。
func requireUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := lookupUser(r)
if user == nil {
// 没有用户,所以重定向到登录页面
http.Redirect(w, r, "/login", http.StatusFound)
return
}
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
dashboard := http.NewServeMux()
dashboard.HandleFunc("/dashboard/hi", printHi)
dashboard.HandleFunc("/dashboard/bye", printBye)
mux := http.NewServeMux()
// 所有以/dashboar开头的路由都要求使用requireUser中间件进行用户身份验证
mux.Handle("/dashboard/", requireUser(dashboard))
mux.HandleFunc("/", home)
http.ListenAndServe(":3000", addRequestID(mux))
}
正如kostix所评论的那样,明智地使用上下文,就像Dave Cheney在“Context
is for cancelation”中建议的那样。
英文:
See "Exploring the context package", using WithValue
and the context associated to the Request:
> ## Middleware
>
> Middleware in Go refers to an http handler which wraps around a multiplexer. There are several 3rd party middleware solutions (such as negroni), but really the standard library supports a very similar pattern. The use of a Context in the request allows us to hold data in the request.
>
> See the example code for invocation and definition.
func putClientIPIntoContext(r *http.Request) context.Context {
ci := r.RemoteAddr
fwd := r.Header.Get("X-Forwarded-For")
if fwd != "" {
ci = fwd
}
ctx := context.WithValue(r.Context(), ClientIPKey, ci)
return ctx
}
> The Context can store request-scoped variables.
It’s useful when writing ‘middleware’, but it’s a little bit ‘anti-pattern’ — it’s a bit magical, because it’s not type-safe.
See more at "Pitfalls of context values and how to avoid or mitigate them in Go".
> The example below only shows how you might use the authentication logic from above to verify that when a user is logged in when visiting any page with a path prefix of /dashboard/
.
A similar approach could be used to verify that a user is an admin before allowing them access to any page with a path prefix of /admin/
.
func requireUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := lookupUser(r)
if user == nil {
// No user so redirect to login
http.Redirect(w, r, "/login", http.StatusFound)
return
}
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
dashboard := http.NewServeMux()
dashboard.HandleFunc("/dashboard/hi", printHi)
dashboard.HandleFunc("/dashboard/bye", printBye)
mux := http.NewServeMux()
// ALL routes that start with /dashboard/ require that a
// user is authenticated using the requireUser middleware
mux.Handle("/dashboard/", requireUser(dashboard))
mux.HandleFunc("/", home)
http.ListenAndServe(":3000", addRequestID(mux))
}
As kostix comments, use Context wisely, like Dave Cheney suggest in "Context
is for cancelation"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论