5

I am using context to attach user payload(userId specifically) in a middleware for my go rest application

// middleware
    // attaching payload to the request context
        claimsWithPayload, _ := token.Claims.(*handlers.Claims)
        ctx := context.WithValue(r.Context(), "userid", claimsWithPayload.Id)
        req := r.WithContext(ctx)
        h := http.HandlerFunc(handler)
        h.ServeHTTP(w, req)

And later in a http handler, I need to extract that userid as string/integer but context().Value() returns an interface{}

// handler 
a := r.Context().Value("userid") // THIS returns an interface{}
response := []byte("Your user ID is" + a) //  how do I use it as a string/integer??
w.Write(response)
Karan Kumar
  • 2,678
  • 5
  • 29
  • 65
  • 2
    You need to [type-assert](https://go.dev/ref/spec#Type_assertions) the `interface{}` value to the correct type. The correct type is the type of the value that you stored with the "userid" key, i.e. the type of `claimsWithPayload.Id`. – mkopriva Jan 10 '22 at 11:19
  • 2
    Does this answer your question? [Convert interface{} to int](https://stackoverflow.com/questions/18041334/convert-interface-to-int) – linuskmr Jan 10 '22 at 11:20
  • Yes, thank you!! – Karan Kumar Jan 10 '22 at 11:27

1 Answers1

7

You can use a type assertion to get the context value as its underlying type:

a := r.Context().Value("userid").(string)

This will panic if the value stored by the middleware is not a string, or if something else sets the context key to something which is not a string. To guard against this you should never use builtin types as context keys, instead define your own type and use that:

type contextKey string
const userIDKey contextKey = "userid"
...
ctx := context.WithValue(r.Context(), userIDKey, claimsWithPayload.Id)
...
a := r.Context().Value(userIDKey).(string)

Because contextKey and userIDKey are unexported, only your package can read or write this value from or to the context.

Bracken
  • 989
  • 8
  • 21
  • So we are indirectly creating a new type which is nothing but a plain string type? How will this solve a problem that you mentioned where `if the value stored by the middleware is not string`?? – Karan Kumar Jan 10 '22 at 11:31
  • 2
    @KaranKumar type-assertions support the "comma-ok" idiom, that means, that to avoid a panic you can do `a, ok := val.(string)`. If `val` is `nil` or something other than `string` the `ok` variable will be set to `false` and `a` will be set to `""`. If `val` is a `string` then `ok` will be set to `true` and `a` will be set to the string value. – mkopriva Jan 10 '22 at 11:38
  • @KaranKumar if your code and only your code can access the context key, then only your code can set the context value. Thus it follows that if you only and always set the context value to type `string` then you can also safely use single value type assertion, because it is not possible for it to be any other type. – Bracken Jan 10 '22 at 12:25