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

How can I define custom error message? #559

Closed
bakbuz opened this issue Dec 20, 2019 · 14 comments
Closed

How can I define custom error message? #559

bakbuz opened this issue Dec 20, 2019 · 14 comments
Assignees
Labels

Comments

@bakbuz
Copy link

bakbuz commented Dec 20, 2019

How can I define custom error message? I don't want to make a detailed explanation. I just want to make a brief statement.

For example:

type RegisterRequest struct {
	Username    string `validate:"required"` 		// UsernameIsRequired
	Email       string `validate:"required,email"` 		// EmailIsRequired,EmailIsNotValid
	Password    string `validate:"required,gte=8,lte=32"`   // PasswordRequired,PasswordGreaterThan8,PasswordLessThan32
}
@deankarn
Copy link

@bakbuz take a look at the example of the translation for custom error message by locale https://github.com/go-playground/validator/blob/master/_examples/translations/main.go it's geared towards more general messages for each tag.

If you need more custom messages I recommend creating your own messaging layer using all the information returned by each error, see the simple example for details https://github.com/go-playground/validator/blob/master/_examples/simple/main.go#L69

@deankarn deankarn self-assigned this Dec 24, 2019
@bakbuz
Copy link
Author

bakbuz commented Dec 27, 2019

Thank you very much.

@bakbuz bakbuz closed this as completed Dec 27, 2019
@bilalcaliskan
Copy link

Defining custom error messages are unneccessarily hard, i do not know why.

@krazik-intuit
Copy link

I agree @bilalcaliskan!

multiple steps to define which seems not ideal and then I still have to process/translate the errors that are returned from the library keeping a "global" translation instance around. I don't understand why the libary can't handle the translation internally before return an error.

ie: https://github.com/go-playground/validator/blob/master/_examples/translations/main.go#L126.

🤯

@johngtrs
Copy link

A guy helped me on stackoverflow to easily create my custom error messages by watching the current tag.

type ApiError struct {
	Param   string
	Message string
}

type User struct {
	FirstName      string `binding:"required"`
	LastName       string `binding:"required"`
	Age            uint8  `binding:"gte=0,lte=130"`
	Email          string `binding:"required,email"`
	FavouriteColor string `binding:"iscolor"`
}

func msgForTag(fe validator.FieldError) string {
	switch fe.Tag() {
	case "required":
		return "This field is required"
	case "email":
		return "Invalid email"
	}
	return fe.Error() // default error
}

// Gin handler in my case
func handler(c *gin.Context) {
	var u User
	if err := c.ShouldBindQuery(&u); err == nil {
		c.JSON(http.StatusOK, gin.H{"message": "Good Job"})
	} else {
		var ve validator.ValidationErrors
		if errors.As(err, &ve) {
			out := make([]ApiError, len(ve))
			for i, fe := range ve {
				out[i] = ApiError{fe.Field(), msgForTag(fe)}
			}
			c.JSON(http.StatusBadRequest, gin.H{"errors": out})
		}
		return
	}
}

And the output will be:

{
    "errors": [
        {
            "Param": "FirstName",
            "Message": "This field is required"
        },
        {
            "Param": "LastName",
            "Message": "This field is required"
        },
        {
            "Param": "Email",
            "Message": "Invalid email"
        },
        {
            "Param": "FavouriteColor",
            "Message": "Key: 'User.FavouriteColor' Error:Field validation for 'FavouriteColor' failed on the 'iscolor' tag"
        }
    ]
}

@vladyslav2
Copy link

vladyslav2 commented Sep 2, 2023

@johngtrs John, i'm trying to run your example and getting an error

➜  escrow-worker git:(master) ✗ go run main.go
# command-line-arguments
./main.go:30:12: fe.Error undefined (type validator.FieldError has no field or method Error)

my guess validator API changed? how do you do it now?

My bad, for some reason go get installed v9 (I do have quite old project and looks like someone already tried to use validator before)

after code clean up + go mod tidy i'm no longer seeing an error

@seeff00
Copy link

seeff00 commented Sep 27, 2023

Is there any solution for custom message for other type different than 'string'. For example int, uint, float, float64 etc ...???

@eugennicoara
Copy link

You might find this useful.

@Code-Horror
Copy link

Fiber has an example on how to do this: https://docs.gofiber.io/guide/validation/

@aliml92
Copy link

aliml92 commented Dec 29, 2023

In my echo app, I used the similar pattern:

func BindAndValidate(c echo.Context, req interface{}) error {
	if err := c.Bind(req); err != nil {
		return err
	}

	if err := c.Validate(req); err != nil {
		
		if _, ok := err.(*validator.InvalidValidationError); ok {
			log.Error(err)
			return errors.New("Something went wrong. Please try again later.")
		}
		
		var verr validationErrors
		for _, err := range err.(validator.ValidationErrors) {
			var e error
			switch err.Tag() {
			case "required":
				e = fmt.Errorf("Field '%s' cannot be blank", err.Field())
			case "email":
				e = fmt.Errorf("Field '%s' must be a valid email address", err.Field())	
			case "eth_addr":
				e = fmt.Errorf("Field '%s' must  be a valid Ethereum address", err.Field())
			case "len":
				e = fmt.Errorf("Field '%s' must be exactly %v characters long", err.Field(), err.Param())
			default:
				e = fmt.Errorf("Field '%s': '%v' must satisfy '%s' '%v' criteria", err.Field(), err.Value(), err.Tag(), err.Param())
			}
			verr = append(verr, e)
		}

		return verr
	}

	return nil
}


type validationErrors []error

func (ve validationErrors) Error() string {

	buff := bytes.NewBufferString("")

	for i := 0; i < len(ve); i++ {

		buff.WriteString(ve[i].Error())
		buff.WriteString("\n")
	}

	return strings.TrimSpace(buff.String())
}

@bakbuz
Copy link
Author

bakbuz commented Jan 27, 2024

Thanks everybody

@chirag1807
Copy link

func InitReqDataValidationTranslation() {
Validate = validator.New()
en := en.New()
uni := ut.New(en, en)
Translator, _ = uni.GetTranslator("en")

Validate.RegisterTranslation("min", Translator, func(ut ut.Translator) error {
	return ut.Add("min", "{0} field must be at least {1} long.", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
	t, _ := ut.T("min", fe.Field(), fe.Param())
	return t
})

}

Here is my code for custom message for min tag.
Now, I want to define two other custom messages for the 'min' tag combined with 'number' and 'min' combined with 'alpha'. So, if any field of a struct has both 'min' and 'number' tags, then show the corresponding message. Similarly, if it has both 'min' and 'alpha' tags, then show the corresponding message.
How can I achieve this functionality?

Thanks in Advance.

@distinctdan
Copy link

This seems way harder than it should be. It covers the advanced translation case, but it doesn't provide an easy way to do the simple use case like most other validation libraries provide. This should be as simple as:

type MyStruct struct {
  Name   string      `validate:"required",validateMsg:"A name is required"`    
  ADate  civil.Date  `validate:"customCivilDate",validateMsg:"Must be in the format YYYY-MM-DD"`
}

My desired behavior is for Validate to generate all error messages with a single call, because if any extra parsing is required after the initial call, then that extra parsing and translation logic will be duplicated in every place that requires validation. It's good that validator allows custom error handling, but I would suggest that the default should be the most convenient case. For translation, this might look like passing the language code in the call like err := Validate(myStruct, "en-US") and it automatically localizes the strings, or it includes the translation as a separate property for each error.

@migueleliasweb
Copy link

It is puzzling how difficult this is by default. Really.

This is my attempt to make this a bit less painful.

This is an untested snippet of a translator that would read from the msg tag that can be added via a custom validator.

enLocale := en.New()
translator, _ := ut.New(enLocale, enLocale).GetTranslator("en")

validate.RegisterTranslation("msg", translator,
  func(ut ut.Translator) error {
	  return ut.Add("msg", "{0}", true)
  },
  func(ut ut.Translator, fe validator.FieldError) string {
	  s, _ := ut.T("msg")
	  return s
  },
)

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

No branches or pull requests