Documentation
¶
Overview ¶
Package validation turns go-playground/validator's struct-tag errors into the same shape request.Validator already uses: request.FieldErrors, one message per JSON field, ready for response.ValidationError.
Why this exists ¶
go-playground/validator's own Error() text is meant for a developer reading a log, not for a client reading a JSON response. Sent as-is (for example map[string]string{"validation": err.Error()}), it has three problems:
- It leaks Go identifiers. A struct field named ResourceID reports as "ResourceID", not the JSON key resource_id a client actually sent.
- It is a single opaque string, not a map keyed by field, so a client cannot point a form error at the right input without parsing English prose.
- It cannot be localized: the wording is baked into the library, not configurable per deployment.
This package fixes all three: Struct maps each failing field to its JSON key (via a validator.RegisterTagNameFunc backed by the json tag, matching how encoding/json itself names fields) and a message drawn from Messages, which — like request.Messages — a consuming application configures once via WithMessages.
Usage ¶
Build one *Validator per application (or use the package-level functions, which use a default instance with DefaultMessages) and call Struct or Write from a handler:
if !validation.Write(w, r, &in) {
return
}
Write reports true when in is valid; on a validation failure it writes a response.ValidationError with the per-field messages and returns false. A non-struct or nil argument is a programming error, not a client mistake: Write logs nothing (it has no logger dependency) and instead writes a generic 500 via response.Error, leaving the underlying error for the caller to inspect by calling Struct directly if it wants to log it.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Messages ¶
type Messages struct {
// Required is used when a field fails the "required" tag, and equally for
// every conditional variant go-playground/validator ships —
// "required_if", "required_unless", "required_with", "required_with_all",
// "required_without" and "required_without_all" — since all of them
// report the same thing to a client: this field must be present. The
// condition that made it required (another field's value, or another
// field's presence/absence) is deliberately not part of field or this
// signature; a client either did or did not send the field.
Required func(field string) string
// Min is used when a field fails the "min" tag. param is the tag's
// argument, exactly as go-playground/validator reports it via
// FieldError.Param(). kind is the field's reflect.Kind after
// dereferencing pointers (FieldError.Kind() already does this), because
// "at least 3" means a different thing for a string (characters), a
// slice/array/map (items) and a number (its value).
Min func(field, param string, kind reflect.Kind) string
// Max mirrors Min for the "max" tag.
Max func(field, param string, kind reflect.Kind) string
// OneOf is used when a field fails the "oneof" tag. param is the tag's
// argument exactly as go-playground/validator reports it — the allowed
// values separated by single spaces (e.g. "draft sent paid").
OneOf func(field, param string) string
// Default is used for any validator tag with no dedicated field above
// (for example "email" or "gt"). tag is the failing tag's name
// (FieldError.Tag()). The default wording deliberately does not
// interpolate tag into the message: a validator tag name is
// implementation detail, not something a client should see, even though
// it is not a Go struct/field name.
Default func(field, tag string) string
}
Messages holds every user-facing string Struct can produce, one function per go-playground/validator tag this package gives dedicated wording to, plus a Default fallback for any other tag.
A library must not bake user-facing copy in one language: DefaultMessages returns neutral English defaults, and a consuming application configures its own copy once, via New(WithMessages(...)).
func DefaultMessages ¶
func DefaultMessages() Messages
DefaultMessages returns the neutral English defaults used when a Validator is built with no WithMessages option.
type Option ¶
type Option func(*Validator)
Option customizes a Validator built with New.
func WithMessages ¶
WithMessages overrides the default user-facing messages. Any field left nil keeps the DefaultMessages value — the same merge rule as request.WithMessages — so a caller only needs to set the fields it wants to localize.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator wraps a go-playground/validator instance configured to name its fields after their JSON tags, plus the Messages used to translate a failing tag into a client-facing string.
func New ¶
New builds a Validator using validator.WithRequiredStructEnabled (so a struct field itself can carry "required", not only its members) and jsonTagName (so every reported field name is the JSON key a client actually sent, not the Go struct field name).
func (*Validator) Struct ¶
func (v *Validator) Struct(val any) (request.FieldErrors, error)
Struct validates v and reports (nil, nil) when it is valid.
On a validation failure (validator.ValidationErrors), it returns one FieldErrors entry per failing field, keyed by the JSON path leading to the field (nested structs and dive'd slices/maps included, e.g. "address.street" or "items[0].monto") — see fieldKey for exactly how that path is derived, including how an embedded (anonymous) struct field flattens into its parent exactly as encoding/json would — and valued with a message drawn from v's Messages.
Any other error — chiefly *validator.InvalidValidationError, returned when v is not a struct, is nil, or is a nil pointer — is a programming error, not a client validation failure, and is returned as-is. Never send its Error() text to a client: it names Go types, not request fields.
func (*Validator) Write ¶
Write validates v and reports true when it is valid, writing nothing to w.
On a validation failure it writes a response.ValidationError with the per-field messages Struct produced and returns false. On a programming error (see Struct) it writes a generic 500 via response.Error using response.CodeInternalError, never the underlying Go error text, and returns false. Write has no logger dependency — it cannot log the underlying error itself — so a caller that wants the error logged should call Struct directly instead:
fields, err := v.Struct(&in)
if err != nil {
logger.Error("validating request", "error", err)
response.Error(w, r, http.StatusInternalServerError, response.CodeInternalError, "failed to validate request")
return
}
if fields != nil {
response.ValidationError(w, r, fields)
return
}