嵌套结构体字段必须显式定义validate tag,且每层都需独立添加;ReadBody不自动校验,须手动调用validator.Struct并检查nil指针;中间件统一处理可避免遗漏,同时需确保json tag与前端字段名严格匹配。

嵌套结构体字段必须显式定义 validate tag,否则 ReadBody 不校验
Iris 的 ctx.ReadBody() 本身不触发任何验证逻辑,哪怕你写了 validate:"required" 这类 tag,它也只当普通注释。嵌套字段(比如 User.Profile.Address.Street)更不会自动递归校验——struct tag 不会穿透多层指针或嵌套结构生效。
正确做法是:每一级嵌套结构体都独立加 tag,并在顶层 handler 或中间件里手动调用校验函数。例如:
type Address struct {
Street string `json:"street" validate:"required,min=5"`
ZipCode string `json:"zip_code" validate:"required,len=6"`
}
type Profile struct {
Nickname string `json:"nickname" validate:"required"`
Address Address `json:"address"`
}
type UserReq struct {
Name string `json:"name" validate:"required"`
Profile Profile `json:"profile"`
}
注意:Address 和 Profile 都必须是值类型或非 nil 指针;若定义为 *Address 且请求中该字段为 null,ReadBody 会跳过赋值,后续校验时直接 panic 或空指针解引用。
用 validator 包做递归校验要手动展开,不能依赖反射自动遍历
第三方包如 go-playground/validator/v10 支持 Struct() 递归校验,但 Iris 不集成它,你得自己调用。而且默认行为对 nil 嵌套字段不友好——它不会报错,而是静默跳过,导致本该失败的请求被放过。
- 必须先检查嵌套字段是否为 nil(比如
req.Profile == nil),再决定是否调validate.Struct(req.Profile) - 错误收集要用
error而不是panic,否则中间件里无法统一返回 400 - 嵌套 map 或 slice 字段(如
map[string]Profile)validator 不支持开箱即用,得转成 slice 后逐个校验
示例片段:
if req.Profile == nil {
ctx.StatusCode(400)
ctx.JSON(map[string]string{"error": "profile is required"})
return
}
if err := validate.Struct(req.Profile); err != nil {
ctx.StatusCode(400)
ctx.JSON(map[string]string{"error": "invalid profile fields"})
return
}
中间件里统一拦截嵌套验证失败,避免每个 handler 重复写 if
把嵌套校验塞进每个 handler 容易漏掉某一层,也违背单一职责。推荐用中间件,在路由匹配后、handler 执行前完成全部结构体校验。
关键点:
- 中间件需接收一个“校验目标结构体类型”作为参数,比如
ValidateJSON[UserReq] - 用
ctx.Request().Header.Get("Content-Type")确保是application/json,否则直接 415 - 调
ctx.ReadBody(&v)后立即校验,失败则调ctx.StopExecution()阻断后续 handler - 错误响应格式要和业务层一致(比如统一用
{"code":400,"msg":"..."})
这样,所有带嵌套字段的 POST/PUT 接口只要注册这个中间件,就不用再管底层怎么校验了。
嵌套字段名大小写不一致会导致绑定失败,进而让验证失效
前端传 street_name,后端结构体字段叫 StreetName,但没加 json:"street_name" tag,ReadBody 就不会赋值——字段保持零值,后续校验 min=5 时发现是空字符串,报错,但真实原因是绑定失败,不是业务规则不满足。
容易忽略的细节:
- 嵌套结构体内部字段也要补全
jsontag,不能只在顶层加 - 如果用了
map[string]interface{}接收动态嵌套,ReadBody无法做类型安全校验,必须手动递归遍历 key 并用validator.Var()逐个校验 - 字段为
nil时,validate.Struct()默认跳过,需额外判断指针是否为 nil
真正难的不是写校验逻辑,而是确保从网络字节流到内存结构体的每一步都不丢字段、不错位。嵌套越深,中间出问题的环节越多。


















