Valores de context: Cuándo y cuándo no
context.WithValue transporta metadatos en el ámbito de la solicitud a través de pilas de llamadas sin pasar diez parámetros de cadena a cada función.
Busca en todas las páginas de la documentación
context.WithValue transporta metadatos en el ámbito de la solicitud a través de pilas de llamadas sin pasar diez parámetros de cadena a cada función.
Usado correctamente, los valores contienen IDs de rastreo y principales de autenticación; usado incorrectamente, se convierten en un mapa global para datos de negocio opcionales.
Almacena metadatos de infraestructura transversales en el contexto con claves tipadas no exportadas.
Pasa las entradas de negocio como parámetros de función explícitos.
Nunca pongas secretos en los valores de contexto sin entender que pueden filtrarse a través de registros e introspección.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
type ctxKey int
const (
requestIDKey ctxKey = iota
userKey
)
func WithRequestID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, requestIDKey, id)
}
func RequestID(ctx context.Context) string {
v, _ := ctx.Value(requestIDKey).(string)
return v
}Cuándo usar esto:
package main
import (
"context"
"fmt"
"log/slog"
)
type ctxKey string
const (
requestIDKey ctxKey = "requestID"
userKey ctxKey = "user"
)
type User struct {
ID string
Role string
}
func withMeta(ctx context.Context, reqID string, u User) context.Context {
ctx = context.WithValue(ctx, requestIDKey, reqID)
return context.WithValue(ctx, userKey, u)
}
func logAction(ctx context.Context, action string) {
attrs := []any{slog.String("action", action)}
if id, ok := ctx.Value(requestIDKey).(string); ok && id != "" {
attrs = append(attrs, slog.String("request_id", id))
}
if u, ok := ctx.Value(userKey).(User); ok && u.ID != "" {
attrs = append(attrs, slog.String("user_id", u.ID))
}
slog.Info("audit", attrs...)
}
func serviceCall(ctx context.Context) {
logAction(ctx, "billing.sync")
}
func main() {
ctx := withMeta(context.Background(), "req-7f3a", User{ID: "u-42", Role: "admin"})
serviceCall(ctx)
fmt.Println("done")
}Lo que esto demuestra:
Value recorre los punteros padres hasta que una clave coincide.WithValue sombrean las claves padres con el mismo tipo y valor.WithValue.| Aceptable | Inaceptable |
|---|---|
| ID de solicitud / rastreo | Manejadores de bases de datos |
| Instantánea del principal autenticado | Cargas útiles de solicitud grandes |
| Idioma o slug de inquilino | Filtros de consulta opcionales |
| Manejador de logger o tracer (con cuidado) | Structs de configuración |
| Indicadores de política de fecha límite (raro) | Secretos en texto plano |
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
u := UserFromJWT(r.Header.Get("Authorization"))
ctx := context.WithValue(r.Context(), userKey, u)
next.ServeHTTP(w, r.WithContext(ctx))
})
}Prefiere tipos de valor pequeños copiados por valor o punteros inmutables.
// El paquete auth posee sus claves; otros paquetes no pueden colisionar
package auth
type key struct{}
var userKey = key{}
func User(ctx context.Context) (User, bool) {
u, ok := ctx.Value(userKey).(User)
return u, ok
}"user". Solución: claves tipadas no exportadas por paquete.*sql.DB a través de constructores, no del contexto.WithValue(ctx, key, nil) aún almacena una entrada. Solución: omite la clave cuando está ausente o usa tipos puntero.| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Parámetro de struct explícito | API clara con pocos campos | Metadatos profundos solo de middleware |
context.Value | IDs transversales entre paquetes | Entradas de lógica de negocio |
| Estilo de hilo local (no en Go) | N/A en Go | - |
| Equipaje de OpenTelemetry | Propagación de rastreo estándar | Monolito simple sin rastreo |
| Encabezados HTTP releídos en la hoja | Microservicios sin estado | Rutas de acceso que llaman repetidamente a los mismos metadatos |
Si necesitas más de un puñado, considera un struct de solicitud explícito.
Los valores de contexto deben ser pequeños y estables.
Algunos equipos lo hacen; otros inyectan loggers a través de constructores.
Si se almacena, usa una interfaz estrecha y evita el estado mutable.
Sí: usa constructores auxiliares en _test.go para adjuntar metadatos de prueba.
Mantiene las claves de producción no exportadas.
No están cifrados ni tienen control de acceso.
No trates el contexto como una bóveda de secretos.
El metadato de gRPC es a nivel de cable; los valores de contexto están en proceso.
Los interceptores a menudo copian metadatos en el contexto para los manejadores.
No automáticamente: serializa campos elegidos en encabezados o metadatos explícitamente.
Recrea los valores en el middleware receptor.
La cancelación y las fechas límite son características de primer nivel del contexto.
Los valores son auxiliares: no los uses para señalar la detención.
Las funciones auxiliares tipadas (User(ctx)) son idiomáticas.
Los genéricos rara vez simplifican más allá de las funciones simples.
No: los contextos hijos heredan la cancelación del padre.
Los valores añaden una capa sin cambiar el comportamiento de Done.
golangci-lint y las listas de verificación de revisión marcan claves de cadena y patrones de contexto como configuración.
Haz cumplir tipos de clave propiedad del paquete en las guías de estilo.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado de GC Green Tea, go fix modernizers - verifica el parche en la compilación), chi (última versión - verifica en la compilación), gin (última versión - verifica en la compilación), echo (última versión - verifica en la compilación), google.golang.org/grpc (última versión - verifica en la compilación), sigs.k8s.io/controller-runtime (última versión - verifica en la compilación), kubebuilder (última versión - verifica en la compilación), tinygo (última versión - verifica los objetivos de la placa en la compilación), wazero (última versión - verifica en la compilación) y golangci-lint (última versión - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026