go-playground / validator

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

Struct fields ignore validation, but struct pointer fields do not #1091

Closed nvelat closed 1 year ago

nvelat commented 1 year ago

Package version eg. v9, v10:

v10

Issue, Question or Enhancement:

When a field with a struct type is present inside the struct we are validating, validation is skipped. This issue does not occur on struct pointers.

Code sample, to showcase or reproduce:

This code should produce 2 errors (both structs failing validation), but instead, the struct without a pointer skips validation of the Baz field, and only one error is produced

package main

import (
    "fmt"

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

type Foo struct {
    Bar string `validate:"required"`
    Baz Baz    `validate:"required"`
}

type FooPointer struct {
    Bar string `validate:"required"`
    Baz *Baz   `validate:"required"`
}

type Baz struct {
    Data string `yaml:"omitempty"`
}

func main() {
    validate := validator.New()

    foo := &Foo{
        Bar: "test",
    }
    // This should error, but it doesn't
    if err := validate.Struct(foo); err != nil {
        fmt.Println(err)
    }

    fooPointer := &FooPointer{
        Bar: "test",
    }
    // errors as expected
    if err := validate.Struct(fooPointer); err != nil {
        fmt.Println(err)
    }
}
zemzale commented 1 year ago

The required tag doesn't work with structs, since there is no way to tell if a struct was initialized or not, because if not provided it defaults to its zero value which is just an empty struct. For example, these two snippets are identical

    foo := &Foo{
        Bar: "test",
        Baz: Baz{},
    }
    foo := &Foo{
        Bar: "test",
    }

The struct pointer works as expected since zero value for a pointer is nil and it fails as per docs

This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.