go - 如何像使用 gorilla 上下文库一样使用 Go 中的新上下文包?

标签 go

Go 附带了一个名为 context 的新包,在最近的版本中(我认为是 Go 1.7)我们应该能够像使用 gorilla/context 包一样使用它:

http://www.gorillatoolkit.org/pkg/context

借助 gorilla 上下文,您可以非常轻松地设置和获取与请求、处理程序和中间件相关的变量。

在 gorilla 上下文中设置一个值真的很容易:

func handleFunc(w http.ResponseWriter, r *http.Request) {
    context.Set(r, "foo", "bar")
}

为了获得值(value)我们可以做的:

func handleFunc(w http.ResponseWriter, r *http.Request) {
    val := context.Get(r, "foo")
}

我知道我们可以在中间件中使用它,以便下一个中间件可以使用在前一个中间件中设置的变量。我希望能够使用 Go 上下文包来做到这一点。

我知道获取一个值很简单,就像这样:

func handleFunc(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.Context().Value("foo"))
}

但我不知道如何设置该值。这对我来说不是很直观,我也不太明白该怎么做。

最佳答案

参见“Exploring the context package”,使用WithValue以及与请求关联的上下文:

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.

在“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))
}

作为kostix评论,明智地使用 Context,比如 Dave Cheney在“Context is for cancelation”中建议

关于go - 如何像使用 gorilla 上下文库一样使用 Go 中的新上下文包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42870507/

相关文章:

go - 从单独的命令/进程共享属性

postgresql - 将足够多的参数传递给lib/pq

json - 将响应数据处理到Struc中

go - 从结构迭代中排除空字段

Goroutines 选择范围循环

go - 从 Go 中的 channel 接收值

php - 无法通过PHP shell_exec在Apache下运行go二进制文件

go - 测量每个流的 gRPC 带宽

go - 检测 Go 例程中断

html - 如何在html文件golang中获取这个值