Documentation
¶
Overview ¶
Package validate checks structs against rules written in `validate` struct tags and reports one message per field.
type Register struct {
Name string `json:"name" validate:"required|max:100"`
Email string `json:"email" validate:"required|email"`
Password string `json:"password" validate:"required|min:12|confirmed"`
PasswordConfirmation string `json:"password_confirmation"`
Role string `json:"role" validate:"in:reader,editor"`
Terms bool `json:"terms" validate:"accepted"`
}
err := validate.Struct(ctx, &in) // nil, *validate.Errors or a rule's error
Handlers built with web.H validate their input automatically after binding, and a failure becomes a 422 response listing the messages.
Tags ¶
The syntax is Laravel's: rules are separated by "|", parameters follow ":" and are separated by commas: `validate:"required|between:3,20|in:a,b,c"`. Rules run in order and stop at the first failure, so each field gets at most one message. `validate:"-"` skips a field and its nested fields.
Rules other than the "required" family and "accepted" skip empty fields, so optional fields only need rules for their format. Nil pointers, blank strings, empty slices and maps, and zero structs (such as a zero time.Time) are empty. Numbers and booleans are never empty, since a zero may be deliberate; use a pointer (*int, *bool) to tell "not sent" from zero.
Keys and labels ¶
Errors are keyed by the name clients use: the json tag, else the form, query, path or header tag, else the Go field name. Nested structs, slices and maps of structs are validated too, with keys like "address.city" and "items.0.name". Messages use a label derived from the key ("first_name" becomes "first name"); set a `label` tag to change it.
Messages ¶
Messages are in the language of the context (package i18n): the catalogs' validation.<rule> messages, English by default, in the style of "The email field must be a valid email address." Labels come from validation.attributes.<key> when a catalog has it. A struct can replace messages with a ValidationMessages method returning templates (or catalog keys, which are translated) keyed by "key.rule" or "rule":
func (Register) ValidationMessages() map[string]string {
return map[string]string{
"email.required": "We need your email to send the receipt.",
"min": "{label} is too short (at least {0}).",
}
}
Templates may use {label}, the rule's parameters {0}, {1}, … and {list} (all parameters joined with ", "). A parameter the catalog has under validation.values.<parameter> is translated ("now").
Custom rules ¶
Register adds a named rule, usually from an init function:
validate.Register("slug", "The {label} field must be a slug.",
func(ctx context.Context, f validate.Field) (bool, error) {
s, _ := f.Value.(string)
return slugRE.MatchString(s), nil
})
Checks that need a database or several fields belong in a Validate(ctx) method on the input type (see web.Validator), which runs after the tag rules pass, or in the handler. Report problems with Fail or an Errors built by hand.
Performance ¶
A struct type's tags are parsed once into a Plan and cached; tag mistakes (unknown rules, bad parameters, rules on the wrong field type, references to missing fields) are reported then, which for web.H means at startup. Validating walks the plan with reflection but never parses tags; with the built-in rules, a valid struct is checked without allocating (maps of structs and file rules excepted).
Fields are resolved like encoding/json resolves them: embedded structs are flattened, shadowed fields are ignored, and a field inside a nil embedded pointer counts as empty.
Index ¶
- func Fail(field, message string) error
- func Register(name, message string, rule Rule)
- func Struct(ctx context.Context, v any) error
- type Errors
- func (e *Errors) Add(field, message string)
- func (e *Errors) Err() error
- func (e *Errors) Error() string
- func (e *Errors) FieldErrors() map[string]string
- func (e *Errors) Get(field string) string
- func (e *Errors) HTTPStatus() int
- func (e *Errors) Has(field string) bool
- func (e *Errors) Keys() []string
- func (e *Errors) Len() int
- func (e *Errors) MarshalJSON() ([]byte, error)
- type Field
- type Plan
- type Rule
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fail ¶
Fail returns an error with one message for field, for checks done in code:
if taken {
return nil, validate.Fail("email", "This email address is already registered.")
}
func Register ¶
Register adds a named rule usable in validate tags, for example "slug" or "starts_with_any:a,b". message is the default message; it may use {label}, and {0}, {1}, … or {list} for the parameters. A catalog's validation.<name> message replaces it (package i18n), and an empty message means validation.custom: "The {label} field is invalid.".
Like sql.Register, Register is meant to be called from an init function, so rules exist before routes compile their validation plans. It panics if the name is not made of letters, digits and underscores, is already registered, or is a built-in rule.
func Struct ¶
Struct validates v, a struct or a pointer to one, using its validate tags. It returns nil, an *Errors listing the failures, or the error of a custom rule that could not run. The plan for v's type is compiled on first use and cached; tag mistakes are returned as errors.
Example ¶
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"errors"
"fmt"
"anetos.dev/anetos/validate"
)
type SignUp struct {
Email string `json:"email" validate:"required|email"`
Password string `json:"password" validate:"required|min:12|confirmed"`
Confirm string `json:"password_confirmation"`
Age *int `json:"age" validate:"min:18"` // optional: nil passes
}
func main() {
err := validate.Struct(context.Background(), SignUp{Email: "ada@", Password: "short", Confirm: "shrt"})
if errs, ok := errors.AsType[*validate.Errors](err); ok {
for _, key := range errs.Keys() {
fmt.Printf("%s: %s\n", key, errs.Get(key))
}
}
}
Output: email: The email field must be a valid email address. password: The password field must be at least 12 characters.
Types ¶
type Errors ¶
type Errors struct {
// contains filtered or unexported fields
}
Errors collects validation failures, at most one message per field. Keys are the field keys used in requests ("email", "address.city", "items.0.name"), so clients can show each message next to its input.
*Errors implements the web package's status and field-error interfaces: returned from a handler, it becomes a 422 Unprocessable Entity response listing the messages. The methods that read accept a nil *Errors.
func (*Errors) Err ¶
Err returns a copy of e as an error, or nil if it has no messages. Later calls to Add on e don't change the returned error. Use it to return an *Errors you built by hand:
var errs validate.Errors
if taken {
errs.Add("email", "This email address is already registered.")
}
return errs.Err()
func (*Errors) FieldErrors ¶
FieldErrors returns a copy of the messages keyed by field.
func (*Errors) HTTPStatus ¶
HTTPStatus returns 422 Unprocessable Entity.
func (*Errors) MarshalJSON ¶
MarshalJSON encodes the messages as an object keyed by field, in order.
type Field ¶
type Field struct {
Key string // request key, e.g. "address.city"
Label string // human-readable name used in messages
Value any // the field's value, with pointers dereferenced (nil for a nil pointer)
Params []string // rule parameters from the tag, e.g. ["3"] for "slug_max:3"
Parent any // the struct that contains the field
}
Field describes the field a custom Rule is checking.
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
Plan is the compiled validation for one struct type. Compile it once (Struct and web.H cache plans) and reuse it; a Plan is safe for concurrent use.
func Compile ¶
Compile builds (or returns the cached) plan for struct type t. It reports unknown rules, bad parameters, rules that don't apply to a field's type, references to missing fields and unknown message keys.
func MustCompile ¶
MustCompile is like Compile but panics on error. Use it at startup.