Skip to content

核心概念

带你快速逛一遍 govalid 的基础组件。读完这一页,剩下的文档会变得 显而易见。

校验循环

调用 govalid.Check(v) 会做以下几件事:

  1. 用反射把 v 解析为一个结构体值。指针会被解引用;nil 不会 panic, 而是直接返回 (nil, true)
  2. 遍历每个导出字段,递归进入 struct 字段和 struct 切片。
  3. 对每个有 valid 标签的字段,解析规则列表,按顺序对该字段的值 执行每个校验器。
  4. 收集每个校验器返回的 *ErrContext
  5. 如果该值(或其指针形式)实现了 Validate() error,最后调用它, 并追加任何非 nil 的错误。
  6. 返回 (errs, len(errs) == 0)

标签

govalid 读取三个结构体标签。它们的名字可以通过 govalid.RulesFieldgovalid.LabelFieldgovalid.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),按以下顺序 解析:

  1. 调用方传入的 locale:Check(v, language.English)
  2. 未注册的 locale → 回退到默认 locale。
  3. 未注册的规则 → 回退到该 locale 的 _unknownErrorTemplate

通过 SetMessageTemplates 覆盖或扩展模板:

go
govalid.SetMessageTemplates(map[string]string{
    "required": "must not be empty",
}, language.English)

完整生命周期见 国际化

required 到底意味着什么

required 是大家最容易理解错的规则。govalid 的定义如下:

字段类型required 失败的条件
stringslicearraymapchan长度为 0
pointerinterfacefunc值为 nil
其他可比较的类型值等于零值
不可比较的 struct 类型跳过(不会 panic)

这一点对 bool 字段很重要——requiredbool 上等价于"必须为 true"。如果你只想要"任意值都行,必须存在",那就别用这条规则。

基于 MIT 协议发布。