negroni中间件中设置的请求上下文在嵌套大猩猩Subrouter中丢失

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.

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)

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:

  1. Use getters and setters that are typed
  2. "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
}