核心概念
带你快速逛一遍 govalid 的基础组件。读完这一页,剩下的文档会变得 显而易见。
校验循环
调用 govalid.Check(v) 会做以下几件事:
- 用反射把
v解析为一个结构体值。指针会被解引用;nil 不会 panic, 而是直接返回(nil, true)。 - 遍历每个导出字段,递归进入 struct 字段和 struct 切片。
- 对每个有
valid标签的字段,解析规则列表,按顺序对该字段的值 执行每个校验器。 - 收集每个校验器返回的
*ErrContext。 - 如果该值(或其指针形式)实现了
Validate() error,最后调用它, 并追加任何非 nil 的错误。 - 返回
(errs, len(errs) == 0)。
标签
govalid 读取三个结构体标签。它们的名字可以通过 govalid.RulesField、govalid.LabelField 和 govalid.MessageField 进行配置,方便和其他标签命名空间共存。
| 标签 | 默认 | 作用 |
|---|---|---|
valid | — | 规则列表,用 ; 分隔。参数跟在 : 后,多个参数用 , 分隔。 |
label | 字段名 | 错误消息中显示的友好名称。支持 label-<lang> 按 locale 覆盖。 |
msg | — | 覆盖该字段第一条失败规则的整个错误消息。 |
完整细节见 标签参考。
校验器
校验器是一个 func(CheckerContext) *ErrContext。govalid 在 包级 Checkers map 中预注册了 17 个:
go
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,
}你可以在运行时替换、扩展或删除这个 map 上的条目。注册必须在并发 调用 Check 之前完成——map 本身不是并发安全的。
CheckerContext
每个校验器都会收到一个完整填充的 context。自定义校验器使用相同的 形态:
go
type CheckerContext struct {
StructValue reflect.Value // 外层 struct
FieldName string // Go 字段名
FieldType reflect.Type
FieldValue interface{} // 待检查的值
FieldLabel string // 已解析的标签
TemplateLanguage language.Tag
Rule *rule // 包含 Rule.params
}ErrContext
失败以 *ErrContext 的形式返回。它实现了 error,所以可以像普通 错误一样被包装、记录或返回:
go
type ErrContext struct {
FieldName string
FieldLabel string
FieldValue interface{}
TemplateLanguage language.Tag
// ... 内部模板状态
}
func (e *ErrContext) Error() string通常你直接遍历返回的切片:
go
errs, ok := govalid.Check(form)
if !ok {
for _, e := range errs {
log.Printf("[%s] %s = %v: %s", e.FieldName, e.FieldLabel, e.FieldValue, e)
}
}模板与 locale
每条规则都关联一个消息模板。govalid 内置两个 locale 字典 (language.Chinese——默认——和 language.English),按以下顺序 解析:
- 调用方传入的 locale:
Check(v, language.English)。 - 未注册的 locale → 回退到默认 locale。
- 未注册的规则 → 回退到该 locale 的
_unknownErrorTemplate。
通过 SetMessageTemplates 覆盖或扩展模板:
go
govalid.SetMessageTemplates(map[string]string{
"required": "must not be empty",
}, language.English)完整生命周期见 国际化。
required 到底意味着什么
required 是大家最容易理解错的规则。govalid 的定义如下:
| 字段类型 | required 失败的条件 |
|---|---|
string、slice、array、map、chan | 长度为 0 |
pointer、interface、func | 值为 nil |
| 其他可比较的类型 | 值等于零值 |
| 不可比较的 struct 类型 | 跳过(不会 panic) |
这一点对 bool 字段很重要——required 在 bool 上等价于"必须为 true"。如果你只想要"任意值都行,必须存在",那就别用这条规则。