validator.struct() 总返回 nil 是因未满足三条件:传入结构体指针而非值、字段名首字母大写、validate 标签拼写正确且独立书写;required 仅判零值,空白符需 min=1 或自定义函数。

validator.Struct() 总返回 nil?先检查这三件事
不是 validator 有 bug,而是它根本没读到你的字段。最常见原因就三个:Struct 传的是值不是指针、字段名小写、validate 标签拼错或漏写。
- 必须传结构体指针:
validate.Struct(&req),传req值拷贝会失败 - 字段必须首字母大写:哪怕标签写成
validate:"required",email string也会被完全忽略——不报错、不校验、无日志 - 标签 key 必须是
validate,不是valid、validation或validator;且不能和json标签挤在一起,要留空格:json:"email" validate:"required,email"
required 为什么放过空格?这不是 bug 是设计
required 只判断是否为零值(""),对 " "、" "(全角空格)甚至 "\t\n" 都放行。这不是疏漏,是底层按 Go 类型零值定义的。
- 想过滤纯空白?加
min=1:validate:"required,min=1",效果等价于strings.TrimSpace(s) != "" - 要更严(如禁止首尾空格)?得写自定义函数,
required+min=1是最简有效兜底 - 别指望
omitempty帮忙:omitempty影响序列化,和校验无关
嵌套结构体校验失败?dive 和指针缺一不可
子结构体字段加了 required 却总报错?大概率是没加 dive,或者用了值类型嵌套。
- 嵌套字段必须显式加
dive:Profile Profile `validate:"dive"`,否则Profile.Name的标签完全不生效 - 切片同理:
Users []User `validate:"dive,required"`,只写required只校验 slice 是否为空 - 值类型嵌套(如
Child Child)遇到空对象{Child: {}}就会触发子字段required——因为非 nil 就进校验。解法是改用指针:Child *Child `validate:"omitempty"`,传 nil 才跳过
Gin 的 ShouldBind 不报错?它根本不管 required
ShouldBind 只做反序列化,不做校验。字段缺失、空字符串、零值,它都默默接受并填入结构体,required 标签形同虚设。
- 必须显式加
binding:"required"(注意是binding,不是validate)才能拦截缺失字段 -
Age *int `binding:"required"`永远不会失败——required对 nil 指针无效。要用exists:Age *int `binding:"exists"`检查 key 是否存在,或改用Age int `binding:"required,exists"` - 自定义验证器注册必须在
gin.Default()之后立即执行,放init()里大概率失效,因为此时 validator 引擎还没初始化
Validate.Struct() 返回的 validator.ValidationErrors 是接口类型,直接 err.Error() 无法区分字段,必须类型断言后遍历提取 Field() 和 Tag() 才能生成前端可读的提示。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











