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

GoGolint

Go Problem Overview


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?

Go Solutions


Solution 1 - Go

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.

Solution 2 - Go

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.

Solution 3 - Go

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())

Solution 4 - Go

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsnorberhuisView Question on Stackoverflow
Solution 1 - GoAinar-GView Answer on Stackoverflow
Solution 2 - GoF566View Answer on Stackoverflow
Solution 3 - GoRuNpiXelruNView Answer on Stackoverflow
Solution 4 - Goyogesh_desaiView Answer on Stackoverflow