validator校验结构体字段必须满足三个硬性前提:字段导出(首字母大写)、标签语法正确、显式调用validate.struct();漏掉任一环节都会静默失效。

validator 包校验结构体字段必须满足三个硬性前提:字段导出、标签语法正确、显式调用 Validate.Struct();漏掉任一环节都会导致校验静默失效,而不是报错。
结构体字段必须首字母大写才能被 validator 读到
validator 依赖反射访问字段,只能读取导出字段(即首字母大写)。小写字段哪怕打满 validate:"required,email" 标签,也会被完全忽略——不报错、不校验、无提示。
- 错误写法:
type User { email string `validate:"required"` }→email不参与任何校验 - 正确写法:
type User { Email string `json:"email" validate:"required,email"` } -
json:标签只影响序列化,和字段是否导出无关;validate:标签生效的前提是字段可被反射读取 - 嵌套结构体同理:内层
Child的字段也必须大写,否则Child.Name不会校验
validate:"required" 不等于非空字符串,空格会被放过
required 对 string 类型只判断是否为零值(即 ""),但用户提交 " " 或 " "(全角空格)时仍会通过。这不是 bug,是设计行为。
- 典型问题:前端输入框只按了空格就提交,后端却认为“有值”
- 解决方案不是弃用
required,而是组合min=1:validate:"required,min=1" -
min=1实际效果等价于strings.TrimSpace(s) != "",能过滤纯空白 - 若需更严格(如禁止首尾空格),得自定义验证函数,
required+min=1是最简有效兜底
嵌套结构体校验失败?先看是不是缺了 omitempty 或指针类型
validator 默认对非 nil 的嵌套结构体做深度校验,哪怕它所有字段都是零值(Name: "", Age: 0),子字段的 required 仍会触发。
- 现象:父结构体传了
{Child: Child{}},子字段Child.Name加了required,结果校验失败 - 原因:
Child是值类型,非nil就进校验;而omitempty对值类型无效 - 解法一(推荐):把字段改为指针类型 +
omitempty:Child *Child `validate:"omitempty"`,传nil则跳过整个子校验 - 解法二:若必须用值类型,且允许空对象,则子字段别加
required,改由业务逻辑判断语义是否为空 - 切片/Map 嵌套需加
dive:Users []User `validate:"dive"`,否则Users[0].Name的标签不生效
HTTP 请求解析后立刻校验,别等进业务逻辑再判断
校验时机错位是高频失误:有人在 json.Unmarshal 后直接读字段、拼 SQL 或调用 service,直到报错才回头补 if 判断。这会导致无效数据污染流程,且难定位源头。
- 正确顺序:
json.Unmarshal() → v.Struct(req) → err != nil 就 return - 不要在 handler 里写
if req.Email == ""这类重复逻辑,每个接口都复制粘贴极易漏边界(比如 Unicode 控制字符) - Gin 用户注意:
c.ShouldBind()默认只认binding:标签;要启用validate:标签,结构体必须同时带json:和validate:,且确保gin.SetMode(gin.ReleaseMode)已配置 - 路径参数(如
/user/{id})和 query 参数不会自动进结构体,需手动转成 int/string 后再用v.Var()单独校验,或封装成独立结构体走v.Struct()
最容易被忽略的是 time.Time 字段:前端传字符串 "2024-01-01",validator 默认不识别,required 会误判为“零时间已存在”。要么用 string 字段配 datetime=2006-01-02 规则,要么在 Unmarshal 后、校验前先做 time.Parse 预处理。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











