validation

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 8 Imported by: 0

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

func Struct

func Struct(v any) (request.FieldErrors, error)

Struct validates v using defaultValidator (DefaultMessages). See (*Validator).Struct.

func Write

func Write(w http.ResponseWriter, r *http.Request, v any) bool

Write validates v using defaultValidator (DefaultMessages) and writes the response. See (*Validator).Write.

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

func WithMessages(m Messages) Option

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

func New(opts ...Option) *Validator

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

func (v *Validator) Write(w http.ResponseWriter, r *http.Request, val any) bool

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
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL