go-playground / validator

:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
MIT License
16.14k stars 1.29k forks source link

panic: Bad field name provided #1161

Closed arthurspa closed 10 months ago

arthurspa commented 10 months ago

Package version: v10.15.3

I can't reproduce with v10.15.1

Issue, Question or Enhancement:

Code panics when validating nested struct with a combination of "dive, unique" validators.

panic: Bad field name provided

Code sample, to showcase or reproduce:

package pkg

import (
    "testing"

    "github.com/go-playground/validator/v10"
)

type ChildStruct struct {
}

type ParentStruct struct {
    ChildStruct []ChildStruct `json:"childStruct" validate:"dive,unique"`
}

func TestCustomValidator_Validate(t *testing.T) {

    validator := validator.New()
    payload := &ParentStruct{
        ChildStruct: []ChildStruct{
            {},
        },
    }
    err := validator.Struct(payload)
    if err != nil {
        t.Errorf("error = %v", err)
    }
}
arthurspa commented 10 months ago

Maybe duplicated of #1147 ?

MysteriousPotato commented 10 months ago

The dive tag is used to specify validation rules for elements. In your example, the unique tag is applied on the elements, not the whole slice. Removing the dive tag should fix the issue.

As a side note, the unique tag won't work if your element type contains a slice or a map since it requires the type to be comparable.

arthurspa commented 10 months ago

@MysteriousPotato , ok, maybe I misused the unique tag. But if I used in the correct way it still panics.

What I want is to define a unique validation for the slice of structs (specific field of the struct). In addition to that, I want to specify dive in order to validate the nested struct. If I remove dive as you suggested then the min validation in the nested struct won't have effect.

Check the code provided bellow.

https://goplay.tools/snippet/Xx2tRZ5wA9X

package main

import (
    "fmt"

    "github.com/go-playground/validator/v10"
)

type ChildStruct struct {
    Id int `json:"id" validate:"min=10"`
}

type ParentStruct struct {
    ChildStruct []ChildStruct `json:"childStruct" validate:"dive,unique=Id"`
}

func main() {
    validator := validator.New()
    payload := &ParentStruct{
        ChildStruct: []ChildStruct{
            {
                Id: 1,
            },
            {
                Id: 2,
            },
        },
    }
    err := validator.Struct(payload)

        // this should display the error that the ChildStruct value does not satisfy the `min` validator
       // but instead the app panics because of the combination of deep and unique
    fmt.Printf("error = %v", err)
}
MysteriousPotato commented 10 months ago

In that case, simply changing the tags order should give you the desired behavior. Tags defined before the dive tag are evaluated against the whole slice.

type DiveBefore struct {
    // Validates that V is not nil and elements have at least 5 chars
    V []string `validate:"required,dive,min=5"`
}

type DiveAfter struct {
    // Validates that V has at least 5 elements and elements are not empty
    V []string `validate:"min=5,dive,required"`
}
arthurspa commented 10 months ago

Great. Changing the order worked for me. I was not aware of that functionality of dive tag. I looked again in the docs and indeed it explains what you just said. Somehow I missed it. Thanks @MysteriousPotato !