Context with value
suggest changeIn HTTP server each request is served by a handler function running in its own goroutine.
We often want to have common per-requested information available in a convenient way.
For example, at the beginning of handling a request we might check a cookie to see if a request is made by a logged in user and we want to have user info available everywhere.
We can do that by using context with value:
type User struct {
Name string
IsLoggedIn bool
}
type userKeyType int
var userKey userKeyType
func contextWithUser(ctx context.Context, user *User) context.Context {
return context.WithValue(ctx, userKey, user)
}
// returns nil if not set
func getUserFromContext(ctx context.Context) *User {
user, ok := ctx.Value(userKey).(*User)
if !ok {
return nil
}
return user
}
// will panic if not set
func mustGetUserFromContext(ctx context.Context) *User {
return ctx.Value(userKey).(*User)
}
func printUser(ctx context.Context) {
user := getUserFromContext(ctx)
fmt.Printf("User: %#v\n", user)
}
func main() {
ctx := context.Background()
user := &User{
Name: "John",
IsLoggedIn: false,
}
ctx = contextWithUser(ctx, user)
printUser(ctx)
}
User: &main.User{Name:"John", IsLoggedIn:false}
For clarity of the example we only show creating a context with value and retrieving value from context.
Because context value is an interface{}
, it’s a good practice to write type-safe wrapper functions for setting and retrieving values.
The key used to set / get value is also an interface{}
. Because context can be passed to functions in code you didn’t write, you want to ensure that the value used for key is unique.
That’s why we defined a non-exported type userKeyType
and used a non-exported global variable userKey
of that type.
This ensures that code outside of our package can’t possibly use this key.
This wouldn’t be true if the key was e.g. a string (or any type available to multiple packages).
We wrote two functions for retrieving the value.
One panics when value is not set, another one returns nil
.
Which one to use is a policy decision for your code.
Sometimes missing a variable on context means a bug in your program and you should use mustGetUserFromContext
variant which panics in that case.
Sometimes missing a variable is expected and you can use getUserFromContext
variant.