如何像使用大猩猩上下文库一样使用Go中的新上下文包?

问题描述:

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.

Go附带了一个名为context的新程序包,并带有最新版本(我认为Go 1.7),我们应该能够 以与大猩猩/上下文包相同的方式使用它: p>

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

使用大猩猩上下文,您可以非常轻松地设置和获取与请求,处理程序和中间件相关的变量 p>

在大猩猩上下文中设置值真的很容易: p>

  func handleFunc(w http.ResponseWriter,r * http.Request  ){
 context.Set(r,“ foo”,“ bar”)
} 
  code>  pre> 
 
 

要获取值,我们可以这样做: p>

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

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

我知道获取值非常简单: p>

  func handleFunc(w http.ResponseWriter,r * http.Request){
 fmt.Println(r.Context()。Value(“ foo”))
} 
  code>  pre> \  n 
 

但是我不知道如何设置该值。 这对我来说不是很直观,我也不怎么知道。 p> div>

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"