ShouldBindJSON默认不校验嵌套结构体字段,需显式添加binding:"required,structonly"或改用指针类型+required,否则子字段如Profile.Bio的required不会触发。

ShouldBindJSON 不能直接处理嵌套结构体的深层校验
当你定义了嵌套结构体(比如 User 包含 Profile),ShouldBindJSON 默认只做一层字段解析,不会自动递归校验子结构体的 binding 标签。例如:
type Profile struct {
Bio string `json:"bio" binding:"required,max=200"`
}
type User struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Profile Profile `json:"profile"`
}
此时 Profile.Bio 的 required 不会触发 —— Gin 默认不展开校验嵌套字段。
- 必须显式启用 validator v10 的递归校验:在嵌套字段上加
binding:"required,structonly"或validate:"dive"(需配合第三方 validator) - 更稳妥的做法是把
Profile改为指针类型:Profile *Profile,再用binding:"required"强制非空,否则空对象会被忽略 - 如果只是想让子结构体也参与校验,别用
ShouldBindJSON,改用ShouldBindWith(&u, binding.JSON)并传入自定义 validator 实例
c.ShouldBind 会消耗 request body,无法多次调用
这是最容易踩的坑:Gin 的 ShouldBind(包括 ShouldBindJSON)底层读取 c.Request.Body,而 HTTP body 是一次性流,读完就 EOF。所以下面这段代码必然失败:
var a FormA
var b FormB
if err := c.ShouldBind(&a); err == nil {
// 成功
} else if err := c.ShouldBind(&b); err == nil {
// 这里永远进不来,因为 Body 已被前一次读空
}
- 解决方法一:用
c.ShouldBindJSON+ 类型判断逻辑,但必须先读一次 body 到内存(ioutil.ReadAll或io.ReadAll),再用json.Unmarshal手动尝试多个结构体 - 解决方法二:用
c.ShouldBindWith(&v, binding.JSON)配合预读的bytes.NewReader(bodyBytes),复用同一份原始数据 - 注意:别在中间件里无意识调用
c.ShouldBind,它会提前耗尽 body,导致后续 handler 绑定失败
binding:"required" 对零值字段行为不一致
binding:"required" 并不等价于“非空”,而是“字段必须出现在请求中”。这意味着:
- JSON 中显式传
"age": 0或"active": false,不会触发 required 报错 - 但若完全不传
age字段,才会报错;而传"age": null时,Go 结构体对应 int 字段会保持 0 值,仍算“已提供”,不报错 - 对指针字段(如
*string),required检查的是指针是否为nil,不是解引用后是否为空字符串 - 若要真正校验“非零”或“非空字符串”,得换用
validator的gt=0、min=1或required_with等规则
ShouldBindQuery 和 ShouldBindUri 不支持嵌套结构体
URL 查询参数(?user.name=john&user.email=x@y.z)和 URI 参数(/users/:id/profile)都不能原生解析嵌套结构体。Gin 的 ShouldBindQuery 和 ShouldBindUri 只支持扁平字段映射。
- 例如
type Params struct { User struct{ Name string } `form:"user.name"` }是无效的,tag 不识别点号路径 - 查询参数只能写成
name=john&email=x@y.z,然后结构体用平铺字段:Name string `form:"name"` - 如果真需要解析嵌套查询参数,得手动用
c.Request.URL.Query()拿到url.Values,再按 key 前缀分组、反序列化 -
ShouldBindUri更严格:只认uri:"id"这种单层绑定,URI 路径里不能带点或斜杠来模拟嵌套
ShouldBind 走到底。


















