validator校验静默失效的三大主因是字段未导出(须首字母大写)、标签语法错误(逗号前后禁空格)、未显式调用validate.struct();小写字段、带空格标签、误用validate.var()均导致不报错也不校验。

validator 标签写错、字段没导出、校验没显式调用,这三件事漏掉任何一件,校验就静默失效——不报错、不拦截、数据照常进业务逻辑。
结构体字段必须首字母大写才能被 validator 读到
validator 依赖反射,只能访问导出字段(即首字母大写)。小写字段哪怕打满 validate:"required,email",也会被完全跳过。
- 错误写法:
type User { email string `validate:"required"` }→email不参与任何校验 - 正确写法:
type User { Email string `json:"email" validate:"required,email"` } - 嵌套结构体同理:内层
Address的City字段也必须大写,否则City的标签无效 -
json标签只影响序列化,和是否导出无关;validate生效的前提是字段可被反射读取
validate 标签语法不能有空格,逗号分隔但无空格
标签值不是自由字符串,,、|、- 都有明确语义,且中间**不能有空格**。写成 required, min=2(带空格)会导致 min=2 被整个忽略。
-
required,min=2,max=20:从左到右执行,required失败就不再检查长度 -
email|url:满足任一即可,注意是竖线|,不是or或英文逗号 -
-:整字段跳过校验,等价于没加标签,不是“空值允许” - 多个 tag 键(如
json:"name" validate:"required")之间用空格分隔,但每个键内部的 value 里不能有多余空格
required 不等于“非空字符串”,它只认 Go 零值
required 判定逻辑是“非零值”,不是“非空字符串”或“非零数字”。对不同类型行为差异很大,容易误判。
-
string:仅""触发失败," "(空格)、"\t\n"全部放过 -
int:零值是0,required对Age int传0永远不报错;业务中0非法?改用gt=0或ne=0 -
*string:只检查指针是否为nil;若指针非nil但值为"",required仍通过,需额外加min=1 -
[]string:空切片[]是合法零值,required不报错;要求数组非空?加min=1
嵌套结构体、切片、map 的校验规则必须区分对待
默认行为差异极大,不加明确控制很容易校验“看似生效实则跳过”。
- 嵌套结构体(如
Address Address):自动递归校验,无需dive;但若想空对象跳过子校验,必须用指针 +omitempty:Address *Address `validate:"omitempty"` - 切片/数组(如
Users []User):必须显式加dive,否则Users[0].Name的标签不生效:Users []User `validate:"dive"` - map(如
Meta map[string]string):需配合keys/endkeys+dive才能校验 key 和 value:Meta map[string]string `validate:"dive,keys,alphanum,endkeys,required,min=1"` -
omitempty只对*T、map、slice、chan、func、interface{}有效;对普通 struct 字段无效
最易被忽略的是:time.Time 字段不能直接加 datetime 标签,因为该规则只对 string 类型生效;还有自定义规则注册必须在 Gin 的 binding.Validator.Engine() 实例上调用,而不是 new 一个新 validator —— 后者根本不会被框架使用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











