Validación con go-playground/validator
encoding/json mapea bytes a tipos de Go; no impone reglas de negocio.
Busca en todas las páginas de la documentación
encoding/json mapea bytes a tipos de Go; no impone reglas de negocio.
github.com/go-playground/validator/v10 (verifica la versión en el momento de la compilación) lee las etiquetas de struct validate y devuelve errores a nivel de campo adecuados para respuestas HTTP 400.
Decodifica JSON primero, luego llama a validator.Struct en el struct poblado.
Las etiquetas expresan restricciones comunes (required, email, gte, oneof) y los validadores personalizados extienden el motor para reglas específicas del dominio.
La validación se mantiene separada de la serialización para que el mismo struct pueda viajar de ida y vuelta a JSON sin incorporar políticas en el formato de transmisión.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import "github.com/go-playground/validator/v10"
var validate = validator.New()
type CreateOrder struct {
SKU string `json:"sku" validate:"required,alphanum,min=3,max=32"`
Qty int `json:"qty" validate:"required,gte=1,lte=1000"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { /* 400 */ }
if err := validate.Struct(req); err != nil {
verrs := err.(validator.ValidationErrors)
// mapear a detalles de problema JSON
}Cuándo usar esto:
json para facilitar la revisión.package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/go-playground/validator/v10"
)
type Signup struct {
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=13,lte=120"`
Country string `json:"country" validate:"required,len=2,alpha"`
Referral string `json:"referral" validate:"omitempty,alphanum"`
}
func formatErrors(err error) string {
var b strings.Builder
for _, fe := range err.(validator.ValidationErrors) {
fmt.Fprintf(&b, "%s failed %s; ", fe.Field(), fe.Tag())
}
return strings.TrimSuffix(b.String(), "; ")
}
func main() {
validate := validator.New()
raw := []byte(`{"email":"not-an-email","age":10,"country":"USA"}`)
var s Signup
_ = json.Unmarshal(raw, &s)
if err := validate.Struct(s); err != nil {
fmt.Println(formatErrors(err))
}
good := Signup{Email: "ada@example.com", Age: 30, Country: "US"}
fmt.Println(validate.Struct(good))
}Lo que esto demuestra:
validator.ValidationErrors expone metadatos de campo, etiqueta y parámetro.omitempty omite las reglas cuando el campo está en cero.| Etiqueta | Significado | Ejemplo |
|---|---|---|
required | Valor no cero | IDs, correos electrónicos |
omitempty | Omitir otras reglas si está en cero | Referencia opcional |
email | Formato de correo electrónico | Registro de usuario |
gte / lte | Límites numéricos | Cantidades |
len | Longitud de cadena/arreglo | Códigos de país |
oneof | Cadenas de enumeración | oneof=red green blue |
dive | Validar elementos de arreglo | validate:"dive,email" |
La lista completa de etiquetas se encuentra en la documentación upstream (verifica la versión en el momento de la compilación).
validate := validator.New()
_ = validate.RegisterValidation("sku", func(fl validator.FieldLevel) bool {
s := fl.Field().String()
return strings.HasPrefix(s, "SKU-")
})Usa etiquetas personalizadas para reglas de dominio que son incómodas como expresiones regulares.
Mantén los validadores puros y rápidos; se ejecutan por solicitud.
http.MaxBytesReader.json.Decoder con DisallowUnknownFields opcional.validate.Struct.gin ofrece etiquetas binding que envuelven al validador; echo tiene enlazadores similares.
Aún puedes usar el validador directamente para código que no sea de framework.
validator.ValidationErrors puede alimentar en_translations o traductores personalizados para mensajes de usuario final.
Registra detalles técnicos en el servidor; devuelve mensajes seguros en el cliente.
// validate:"-" omite un campo por completo (marcas de tiempo establecidas por el servidor)
// Usa punteros para que required pueda distinguir entre ausente y cero para númerosEmpareja con Etiquetas de Struct para JSON, DB y Validación para alinear etiquetas.
Struct. Solución: valida explícitamente en los manejadores.required en cero entero - 0 falla required para enteros. Solución: usa punteros para números opcionales o elimina required cuando 0 sea válido.email no es perfecto - La etiqueta verifica el formato, no la entregabilidad. Solución: agrega flujos de confirmación para cuentas reales.Struct en la raíz; los structs internos necesitan etiquetas validate en los campos anidados. Solución: usa dive para arreglos de DTOs anidados.validator.New por solicitud - Crear el motor en cada solicitud es lento. Solución: reutiliza una instancia validate a nivel de paquete; registra validadores personalizados en init.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| go-playground/validator | APIs HTTP basadas en etiquetas | Reglas de grafo complejas |
| Verificaciones escritas a mano | Manejadores pequeños | Formularios grandes |
| OpenAPI / JSON Schema | Contrato primero en CI | Validación solo en tiempo de ejecución |
| CEL o motores de políticas | Reglas de autorización | Formatos de campo simples |
| Restricciones de Protobuf | gRPC con protovalidate | Solo REST plano |
Después - la des-serialización debe poblar el struct primero.
Sí - vincula la consulta a un struct y llama a Struct.
Los enlazadores de framework a menudo comparten el mismo motor de etiquetas.
Usa structs parciales separados o punteros para que required no se active en campos ausentes.
Usa validación a nivel de struct con RegisterStructValidation.
Mantén la lógica entre campos legible y probada.
Una instancia Validate configurada es segura para llamadas concurrentes a Struct.
Registra validadores personalizados antes de servir tráfico.
Valida en el borde; mantén las restricciones de la base de datos como último recurso.
Las reglas duplicadas son aceptables para defensa en profundidad.
Sí - cualquier struct poblado califica.
Las etiquetas son agnósticas al transporte.
400 con errores de campo legibles por máquina para APIs públicas.
Registra el detalle completo en el servidor.
No ejecuta el validador.
Usa pruebas unitarias por DTO y linters opcionales para alinear nombres de etiquetas.
Prueba en tabla fixtures JSON válidos e inválidos por endpoint.
Verifica las etiquetas de error, no solo la presencia de errores.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado de Green Tea GC, go fix modernizers - verifica el parche en el momento de la compilación), chi (última versión - verifica en el momento de la compilación), gin (última versión - verifica en el momento de la compilación), echo (última versión - verifica en el momento de la compilación), google.golang.org/grpc (última versión - verifica en el momento de la compilación), sigs.k8s.io/controller-runtime (última versión - verifica en el momento de la compilación), kubebuilder (última versión - verifica en el momento de la compilación), tinygo (última versión - verifica objetivos de placa en el momento de la compilación), wazero (última versión - verifica en el momento de la compilación) y golangci-lint (última versión - verifica el conjunto de linters en el momento de la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026