Fix "should not use basic type string as key in context.WithValue" golint
Asked Answered
C

4

94

I am passing an uuid in using the Context and WithValue to subsequent functions that handle this *http.request. This uuid is was passed in the authorization header to a REST call to identify a person. The authorization token is verified and needs to accessible to check if the call is itself is authorized.

I used:

ctx := context.WithValue(r.Context(), string("principal_id"), *id)

But golint complains:

should not use basic type string as key in context.WithValue

What is the best option that could be used to retrieve this key that is not a basic type like a simple string?

Collimore answered 30/11, 2016 at 14:50 Comment(0)
C
128

Just use a key type:

type key int

const (
    keyPrincipalID key = iota
    // ...
)

Since you've defined a separate type, it will never collide. Even if you have two packages, pkg1.key(0) != pkg2.key(0).

See also: Go Blog about key collisions in context.

Cran answered 30/11, 2016 at 14:53 Comment(3)
What if I'm passing the context to a different module (where the unexported key is not available)?Frisse
@Frisse You must provide exported getters and maybe setters. PrincipalIDFromCtx(context.Context) (int64) and CtxWithPrincipalID(context.Context, int64) (context.Context).Cran
Should these methods go in pkg1, pkg2 or both? I see possible cycle dependencies if only in pkg1 or pkg2. Having such method in both makes sense, but that produces duplicated code.Frisse
J
23

Use type struct{} much better.

type ctxKey struct{} // or exported to use outside the package

ctx = context.WithValue(ctx, ctxKey{}, 123)
fmt.Println(ctx.Value(ctxKey{}).(int) == 123) // true

Reference: https://golang.org/pkg/context/#WithValue

The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys. To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables' static type should be a pointer or interface.

Julius answered 23/6, 2021 at 12:55 Comment(1)
I think you should not export the type of key (I mean ctxKey) but you can export the key itself.Corolla
V
7

I achieve the above by doing the following and feel it's pretty clean

package util

import "context"

type contextKey string

func (c contextKey) String() string {
    return string(c)
}

var (
    // ContextKeyDeleteCaller var
    ContextKeyDeleteCaller = contextKey("deleteCaller")
    // ContextKeyJobID var
    ContextKeyJobID contextKey
)

// GetCallerFromContext gets the caller value from the context.
func GetCallerFromContext(ctx context.Context) (string, bool) {
    caller, ok := ctx.Value(ContextKeyDeleteCaller).(string)
    return caller, ok
}

// GetJobIDFromContext gets the jobID value from the context.
func GetJobIDFromContext(ctx context.Context) (string, bool) {
    jobID, ok := ctx.Value(ContextKeyJobID).(string)
    return jobID, ok
}

..and then set on context by,

ctx := context.WithValue(context.Background(), util.ContextKeyDeleteCaller, "Kafka Listener")

..get value from context by,

caller, ok := util.GetCallerFromContext(ctx)
if !ok {
    dc.log.Warn("could not get caller from context")
}
fmt.Println("value is:", caller) // will be 'Kafka Listener'

and can print out value of key by doing,

fmt.Println("Key is:", ContextKeyDeleteCaller.String())
Vaios answered 17/6, 2020 at 6:18 Comment(0)
F
-11

Sharing a brief answer for the above question. GitHub Link In short, context.WithValue() needs interface{} type as keys and values.

I hope this helps. Thank you.

Faulkner answered 27/2, 2019 at 20:12 Comment(3)
I am not sure I get the comment - string should work as a parameter to function that takes input as interface{}Callean
@SaurabhNayar That is no Golang idiomatic way. You would get GoLinter warnings.Faulkner
this doesn't seem actually related to the question. The question is about a linter warning when using plain string types as context keys.Gesellschaft

© 2022 - 2025 — McMap. All rights reserved.