asaskevich / govalidator

[Go] Package of validators and sanitizers for strings, numerics, slices and structs
MIT License
6k stars 556 forks source link

Empty values are not passed to custom validator. #408

Open bambulin opened 3 years ago

bambulin commented 3 years ago

BC Break Report

Empty values are not passed to custom validator.

Q A
Version v0.0.0-20200907205600-7a23bdc65eef

Summary

Empty values are not passed to custom validator. Unable to distinguish nils as not acceptable and empty vals as acceptable.

Previous behavior

In previous versions an empty values were passed to custom validators so it was possible to distinguish between nils and empty strings or false etc. "require" tag accepted empty values as valid.

Current behavior

Currently empty values or nils are not passed to the custom validator, so there's no possibility to distinguish nil as unacceptable value and empty value as acceptable. Especially if the field is a pointer to another struct that contains bools only and we need to distinguish the nil and the struct with falses as acceptable value. The scenario when we consider nil pointer as all falses is not acceptable since this struct is received via JSON and we need to distinguish if the client provided the falses intentionaly or the request is missing the field completely and unintentionally.

There's validator method govalidator.SetNilPtrAllowedByRequired() which enables the exact opposite behavior as desired.

So how to distinguish nils as notacceptable values and empty values as acceptable in new version?

How to reproduce

type Settings struct {
    S1 bool
    S2 bool
}

type CustomNotNil struct {
    NotNil *Settings `valid:"notnil"`
}

func TestNotNil(t *testing.T) {
    customValidatorCalled := false
    govalidator.CustomTypeTagMap.Set("notnil", func(i interface{}, o interface{}) bool {
        customValidatorCalled = true
        return i != nil
    })

    _, err := govalidator.ValidateStruct(&CustomNotNil{&Settings{S1: false, S2: false}}) 

    assert.True(t, customValidatorCalled) // this passes since "notnil" validator is called 
    assert.NoError(t, err)
}

func TestNil(t *testing.T) {
    customValidatorCalled := false
    govalidator.CustomTypeTagMap.Set("notnil", func(i interface{}, o interface{}) bool {
        customValidatorCalled = true
        return i != nil
    })

    _, err := govalidator.ValidateStruct(&CustomNotNil{}) // NotNil field is nil

    assert.True(t, customValidatorCalled) // this fails since "notnil" validator is not called
    assert.Error(t, err)                  // this fails since err is nill
}

type Required struct {
    Required *Settings `valid:"required"`
}

func TestRequired(t *testing.T) {
    _, err := govalidator.ValidateStruct(&Required{&Settings{S1: false, S2: false}})

    assert.NoError(t, err) // this fails since err is not nil because "required" considers struct with falses as empty
}
mrwcjoughin commented 3 years ago

I'm getting this as well.

We are trying to validate 0 values for int's and it is also not calling the custom validator.

Please we need this fixed urgently!

mrwcjoughin commented 3 years ago

I have created a PR to fix this in a non-breaking way https://github.com/asaskevich/govalidator/pull/409

sergeyglazyrindev commented 2 years ago

Hello guys! I forked this package cause owner disappeared. Hope, he will be back, but it would be easier to merge these changes back if he is back Link to my repo: create issue there and we'll discuss it.