You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:35:22