Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom error message to struct field and to struct level validation #1183

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

navamedha
Copy link

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:

  • Tests exist or have been written that cover this particular change.

Created a test --> TestCustomErrorMessages

@go-playground/validator-maintainers

@navamedha navamedha requested a review from a team as a code owner October 20, 2023 06:03
@coveralls
Copy link

Coverage Status

coverage: 73.884% (+0.04%) from 73.849% when pulling b58bb0b on navamedha:custom-err-msg into 94a637a on go-playground:master.

@deankarn
Copy link
Contributor

deankarn commented Nov 4, 2023

@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:

  1. Whether I should use reflection and add the reflect.Value or StructField to the validation failures and let it be extracted dynamically there which may benefit a could other future things.
  2. 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

@zx8
Copy link

zx8 commented Nov 5, 2023

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.

@joseluisq
Copy link

Any plan to move this forward? Also, does this PR consider placeholders in the message?

@deankarn
Copy link
Contributor

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.

@joseluisq
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants