v9
I declared a boolean flag enabled for logical deletion.
When I specified "enabled": false in a request payload, it raised the error.
Error:Field validation for 'Enabled' failed on the 'required' tag
It seems required boolean field will be regarded as an invalid one if it was false.
type Item struct {
ID int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Enabled bool `json:"enabled" validate:"required"`
}
func decodeItem(ctx context.Context, r *http.Request) (*Item, error) {
payload := Item{}
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
return nil, errors.Wrap(err, "[Error] Failed to decode request payload.")
}
// It'll panic with the message: `Error:Field validation for 'Enabled' failed on the 'required' tag`
err = Validator.Struct(payload)
if err != nil {
return nil, errors.Wrap(err, "[Error] Invalid payload.")
}
return &payload, nil
}
Hey @timakin I feel like I've answered this a million times lol, time to add it to the docs or provide example I guess.
ok so here's the explanation, in multiple parts:
Go's static nature doesn't allow nullable/unassigned/undefined types but initializes them to their default values.
required - required under the hood checks if the value of the field is the default value of the datatype; in this case of a bool false.
here's an example of why required doesn't make sense on a regular bool
// json
{ "bool": false }
type Test struct {
Bool bool `json:"bool"`
}
var payload Test
err := json.NewDecoder(r.Body).Decode(&payload)
....
// payload.Bool = false
// and again, but with this json
{}
type Test struct {
Bool bool `json:"bool"`
}
var payload Test
err := json.NewDecoder(r.Body).Decode(&payload)
....
// it will also be payload.Bool = false
// because Go initialized it to it's default value.
So as you can see type bool will always be populated and so required doesn't make sense. Now having said that, you may be looking to validate that the JSON set the bool, then there are things you can do.
change the type to *bool and now the type is a pointer, and it's default value is nil and so required will work as most people expect if the data wasn't in the JSON.
I personally try to model my data so that if no boolean value is passed, it's just like it was passed as false; that way I have no need to see if it was sent and can avoid the *bool altogether, but I know that's not always possible.
Sorry for the long winded explanation, please let me know if this all makes sense or you have any questions. P.S. I will look at adding this to the docs and/or add an example.
Thanks for your clear explanation!
I surely understood that json.Decoder is strongly related to this topic.
It works if I won't attach the required tag to boolean fields, so it's just the question.
If it's on the doc it'll save so many users of this package.
@deankarn the same goes for int/uint default value of 0?
Can't I set the field to required and be able to receive requests with that field initiate to '0' right?
Or there is a way to do it? Except for the trick with the ptr.
PS I think that it will be great to add a section to clarify:
Types and their default values and what can we do about it.
Thanks again :)
Most helpful comment
Hey @timakin I feel like I've answered this a million times lol, time to add it to the docs or provide example I guess.
ok so here's the explanation, in multiple parts:
Go's static nature doesn't allow nullable/unassigned/undefined types but initializes them to their default values.
required- required under the hood checks if the value of the field is the default value of the datatype; in this case of a bool false.here's an example of why
requireddoesn't make sense on a regularboolSo as you can see type
boolwill always be populated and sorequireddoesn't make sense. Now having said that, you may be looking to validate that theJSONset the bool, then there are things you can do.change the type to
*booland now the type is a pointer, and it's default value isniland sorequiredwill work as most people expect if the data wasn't in theJSON.I personally try to model my data so that if no boolean value is passed, it's just like it was passed as false; that way I have no need to see if it was sent and can avoid the
*boolaltogether, but I know that's not always possible.Sorry for the long winded explanation, please let me know if this all makes sense or you have any questions. P.S. I will look at adding this to the docs and/or add an example.