validator包required_if在嵌套结构体父级验证失效问题
go-playground/validator/v10嵌套结构体required_if失效问题解决
问题现象
使用go-playground/validator/v10时,required_if验证规则仅在同层级结构体字段中生效,嵌套结构体引用父结构体字段时失效:
- 同层级的
IRN字段配置required_if=CardType 1,注释IRN:1会触发验证错误,符合预期; - 嵌套结构体
B的Day字段配置required_if=A.CardType 1,注释Day:"01"不会触发验证错误,规则完全失效。
测试代码如下:
import ( "testing" "github.com/go-playground/validator/v10" ) type A struct { CardType int `validate:"required"` IRN uint8 `validate:"required_if=CardType 1,omitempty"` B B `validate:"required"` } type B struct { Day string `validate:"required_if=A.CardType 1"` } func Test(t *testing.T) { m := A{ CardType: 1, IRN: 1, // 注释此行会触发错误 B: B{ Day: "01", // 注释此行无错误,不符合预期 }, } validate := validator.New() err := validate.Struct(m) if err != nil { panic(err) } }
原因
validator的内置required_if规则中,字段引用是基于当前结构体的上下文。嵌套结构体B的验证上下文仅包含自身字段,无法识别A.CardType这种跨层级的父结构体字段引用,因此该规则不会生效。
解决方案
方案1:结构体级别验证函数
在父结构体A上注册结构体级别的验证函数,直接检查CardType与B.Day的关联逻辑:
import ( "testing" "github.com/go-playground/validator/v10" ) type A struct { CardType int `validate:"required"` IRN uint8 `validate:"required_if=CardType 1,omitempty"` B B `validate:"required"` } type B struct { Day string // 移除无效的跨层级标签 } // 自定义结构体级别验证:当CardType为1时,B.Day必须非空 func validateBDayWhenCardType1(sl validator.StructLevel) { a := sl.Current().Interface().(A) if a.CardType == 1 && a.B.Day == "" { sl.ReportError(a.B.Day, "Day", "B.Day", "required_if_cardtype_1", "") } } func Test(t *testing.T) { m := A{ CardType: 1, IRN: 1, B: B{ // Day: "01", // 注释此行会触发验证错误 }, } validate := validator.New() // 注册结构体级别验证函数到A validate.RegisterStructValidation(validateBDayWhenCardType1, A{}) err := validate.Struct(m) if err != nil { panic(err) // 触发错误:Key: 'A.B.Day' Error:Field validation for 'B.Day' failed on the 'required_if_cardtype_1' tag } }
方案2:自定义跨层级验证标签
如果需要复用类似逻辑,可以自定义一个支持引用父结构体字段的验证标签,比如required_if_parent:
import ( "reflect" "strconv" "strings" "testing" "github.com/go-playground/validator/v10" ) type A struct { CardType int `validate:"required"` IRN uint8 `validate:"required_if=CardType 1,omitempty"` B B `validate:"required"` } type B struct { Day string `validate:"required_if_parent=CardType 1"` } // 自定义验证函数:检查父结构体指定字段值满足条件时,当前字段必须非空 func requiredIfParent(fl validator.FieldLevel) bool { // 获取父结构体实例 parent := fl.Parent().Interface() a, ok := parent.(A) if !ok { return true // 非A的子字段,跳过验证 } // 解析标签参数:格式为"父字段名 预期值" params := fl.Param() parts := strings.Split(params, " ") if len(parts) != 2 { return true // 参数格式错误,跳过 } parentFieldName, expectedValStr := parts[0], parts[1] // 获取父结构体字段值 parentField := reflect.ValueOf(a).FieldByName(parentFieldName) if !parentField.IsValid() { return true // 父字段不存在,跳过 } // 比较父字段值与预期值(此处仅处理int类型,可根据需求扩展其他类型) if parentField.Kind() == reflect.Int { parentVal := parentField.Int() expectedVal, _ := strconv.Atoi(expectedValStr) // 父字段值符合预期且当前字段为空时,验证失败 if parentVal == int64(expectedVal) && fl.Field().String() == "" { return false } } return true } func Test(t *testing.T) { m := A{ CardType: 1, IRN: 1, B: B{ // Day: "01", // 注释此行会触发验证错误 }, } validate := validator.New() // 注册自定义验证标签 validate.RegisterValidation("required_if_parent", requiredIfParent) err := validate.Struct(m) if err != nil { panic(err) // 触发错误:Key: 'A.B.Day' Error:Field validation for 'Day' failed on the 'required_if_parent' tag } }
内容的提问来源于stack exchange,提问作者majid
相关产品推荐
相关产品推荐

