Core Concepts
A short tour of the primitives that make up govalid. Read this once and the rest of the docs will feel obvious.
The check loop
Calling govalid.Check(v) does the following:
- Resolve
vto a struct value via reflection. Pointers are dereferenced; nils return(nil, true)instead of panicking. - Walk every exported field, recursing into struct fields and slices-of-structs.
- For each field with a
validtag, parse the rule list and execute every checker in order against that field's value. - Collect every
*ErrContextreturned by a checker. - If the value (or its pointer form) implements
Validate() error, call it last and append any non-nil error. - Return
(errs, len(errs) == 0).
Tags
govalid reads three struct tags. Their names are configurable through govalid.RulesField, govalid.LabelField and govalid.MessageField in case you need to coexist with another tag namespace.
| Tag | Default | Purpose |
|---|---|---|
valid | — | Rule list, ;-separated. Parameters follow :, multiple parameters separated by ,. |
label | field name | Human-friendly name in error messages. Supports label-<lang> per-locale overrides. |
msg | — | Override the entire error message for the field's first failing rule. |
See the Tags Reference for every detail.
Checkers
A checker is a func(CheckerContext) *ErrContext. govalid ships with 17 of them registered in the package-level Checkers map:
var Checkers = map[string]CheckFunc{
"required": required,
"min": min,
"max": max,
"minlen": minlen,
"maxlen": maxlen,
"alpha": alpha,
"alphanumeric": alphaNumeric,
"alphadash": alphaDash,
"username": userName,
"email": email,
"ipv4": ipv4,
"mobile": mobile,
"tel": tel,
"phone": phone,
"idcard": idCard,
"equal": equal,
"list": list,
}You can replace, extend, or remove entries on this map at runtime. Registration must happen before Check is called concurrently — the map itself is not synchronized.
CheckerContext
Each checker receives a fully-populated context. Custom checkers use the same shape as built-ins:
type CheckerContext struct {
StructValue reflect.Value // the surrounding struct
FieldName string // Go field name
FieldType reflect.Type
FieldValue interface{} // the value to inspect
FieldLabel string // resolved label
TemplateLanguage language.Tag
Rule *rule // includes Rule.params
}ErrContext
Failures are returned as *ErrContext. It implements error, so each failure can be wrapped, logged, or returned like any other Go error:
type ErrContext struct {
FieldName string
FieldLabel string
FieldValue interface{}
TemplateLanguage language.Tag
// ... internal template state
}
func (e *ErrContext) Error() stringYou usually just iterate over the returned slice:
errs, ok := govalid.Check(form)
if !ok {
for _, e := range errs {
log.Printf("[%s] %s = %v: %s", e.FieldName, e.FieldLabel, e.FieldValue, e)
}
}Templates and locales
Each rule has an associated message template. govalid bundles two locale dictionaries (language.Chinese — the default — and language.English) and resolves them like this:
- Locale picked at call site:
Check(v, language.English). - Unknown locale → fall back to the default locale.
- Unknown rule → fall back to the locale's
_unknownErrorTemplate.
Override or augment templates via SetMessageTemplates:
govalid.SetMessageTemplates(map[string]string{
"required": "must not be empty",
}, language.English)See Internationalization for the full lifecycle.
What required actually means
required is the rule most people get wrong. govalid's definition:
| Field kind | Required failure means… |
|---|---|
string, slice, array, map, chan | length is 0 |
pointer, interface, func | value is nil |
| Other comparable kinds | value equals the zero value |
| Incomparable struct types | rule is skipped (won't panic) |
This matters for bool fields — required on a bool means "must be true". If you want "either value is fine, just present", drop the rule.