feat: add custom error message to struct field and to struct level validation
Add custom error message to struct field and to struct level validation.
Enhances
struct field
We can now give a custom error message to a struct field by adding a struct tag and give that struct tag to validate.RegisterErrMsgFunc, like we give json struct tag to validate.RegisterTagNameFunc.
Custom error message can be accessed using err.Msg() as following.
type User struct {
Email string `validate:"required,email" msg:"User email is invalid"`
}
v = validator.New()
v.RegisterErrMsgFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("msg")
})
err = v.Struct(&User{})
for _, err := range err.(validator.ValidationErrors) {
// err.Field() --> Email
// err.Msg() --> User email is invalid
// err.Error() --> Key: 'User.e-mail' Error:Field validation for 'e-mail' failed on the 'required' tag
}
struct level
We can now add a custom error message while registering validation at struct level using validate.RegisterStructValidation.
Custom error message can be accessed using err.Msg() as following.
type User1 struct {
Addr1 string
Addr2 string
Addr3 string
}
v1 = validator.New()
v1.RegisterStructValidation(User1StructLevelValidation, User1{})
err = v1.Struct(&User1{})
for _, err := range err.(validator.ValidationErrors) {
// err.Field() --> Addr1
// err.Msg() --> Any one of Addr1 or Addr2 or Addr3 must be provided
// err.Error() --> Key: 'User1.Addr1' Error:Field validation for 'Addr1' failed on the 'addr1oraddr2oraddr3' tag
}
...
func User1StructLevelValidation(sl StructLevel) {
st := sl.Current().Interface().(User1)
if st.Addr1 == "" && st.Addr2 == "" && st.Addr3 == "" {
sl.ReportErrorWithMsg(st.Addr1, "Addr1", "Addr1", "addr1oraddr2oraddr3", "",
"Any one of Addr1 or Addr2 or Addr3 must be provided")
sl.ReportErrorWithMsg(st.Addr2, "Addr2", "Addr2", "addr1oraddr2oraddr3", "",
"Any one of Addr1 or Addr2 or Addr3 must be provided")
// without custom error message
sl.ReportError(st.Addr3, "Addr3", "Addr3", "addr1oraddr2oraddr3", "")
}
}
Make sure that you've checked the boxes below before you submit PR:
- [x] Tests exist or have been written that cover this particular change.
Created a test --> TestCustomErrorMessages
@go-playground/validator-maintainers
coverage: 73.884% (+0.04%) from 73.849% when pulling b58bb0be3e79ca61c61e6ac269e76125b2d2ff41 on navamedha:custom-err-msg into 94a637ab9fbbb0bc0fe8a278f0352d0b14e2c365 on go-playground:master.
@navamedha TY for the PR.
Now that validation tags run properly on structs themselves this is something I've been thinking about adding.
This is a variant, one of many, I've been thinking about implementing and need to give it some thought.
Some context is I'm still thinking about how to handle 2 things:
- Whether I should use reflection and add the
reflect.ValueorStructFieldto the validation failures and let it be extracted dynamically there which may benefit a could other future things. - Still thinking about how to handle validations like so and whether to enforce a single message or allow separate for inter nested ones eg.
map[int]string``validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,required"<- three different errors can occur at 3 different levels
Have been waiting for something like this for a while now!
I've currently pulled this PR in manually and am using it in my go.mod - it's working really well, so great job. 🎉
Of course, I'd love to move back to an official tag sooner rather than later, so am keen to see what @deankarn cooks up before this is eventually merged.
Any plan to move this forward? Also, does this PR consider placeholders in the message?
Sorry all, not sure when I am going to be able to get to this and many other PR’s as I am swamped with other projects at the moment.
I could really use some help maintaining this package.
high level thoughts are I think this is a good stopgap solution for this version of the package. The next version and better option I would change the validations function signature to return error instead and let people customize their error return messaging as they see fit along with stripping out translation dependencies.
Thanks for the update. I guess, the PR is far from ready then. For example, It seems like does not support multiple messages per validation per struct field.
I would like to be able to do something like this.
type UserParams struct {
UserId string `validate:"required(message = 'UserID is not provided'), uuid4(message = 'UserID is not a valid UUID4!')"`
}
In a similar way to how we do it in for example the Rust world. That one should be great to have.