Skip to content

Custom error message #669

Open
Open

Description

@1379

The parameter of the RegisterValidation function is a validation function that returns a Boolean value. If we want to customize the error message, we have to register a translation function, which is too much trouble.
Our verification function may return an error for many reasons, so the error message should also be different, but only based on the returned boolean value, we cannot determine what caused the verification failure, so the error message of the translation function is only One kind.
Why can't you register a check function that returns error directly?
Just like the following.

type Func=func(fl FieldLevel) error
func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {

}

Activity

  1. 1379 commented on Sep 28, 2020

    @1379
    Author

    Only based on the boolean value returned by the validation function, we cannot determine what caused the validation failure, so customizing the error message will be very troublesome

  2. self-assigned this
    on Oct 17, 2020
  3. deankarn commented on Oct 17, 2020

    @deankarn
    Contributor

    @1379 sounds like a great enhancement for the next version of the lib, but unfortunately right now is a breaking change.

  4. 1379 commented on Oct 17, 2020

    @1379
    Author

    @deankarn
    I simply made some changes to the "validator", which is currently supported and used in my own project.
    My implementation is like this:
    Provides two RegisterValidation functions, the first parameter type is still "type Func=func(fl FieldLevel) boolean",
    The parameter type of the second function becomes "type Func=func(fl FieldLevel) error".
    When verifying, you only need to determine which type of verification function is.

  5. pwli0755 commented on Oct 20, 2020

    @pwli0755

    Same need

  6. kke commented on Nov 18, 2020

    @kke

    The custom validation error messages from sl.ReportError() are a bit silly too:

    // sl.ReportError(field interface{}, fieldName string, structFieldName string, tag string, param string)
    sl.ReportError(hosts, "hosts", "this does nothing", "some error message here", "this does nothing")
    

    ==>

     Key: 'ClusterConfig.Spec.hosts' Error:Field validation for 'hosts' failed on the 'some error message here' tag
    

    If it was not from a tag, but a custom StructValidation , it should say something like:

     Key: 'ClusterConfig.Spec.hosts' Error:Field validation for 'hosts' failed: some error message here
    
  7. kevin2027 commented on Sep 18, 2021

    @kevin2027

    here is a way to fix it #837

  8. pbikki commented on Apr 13, 2023

    @pbikki

    same need. especially for sl.ReportError() on struct validation to include custom error messages to user

  9. joostvdhoff commented on Sep 4, 2024

    @joostvdhoff

    I think it's possible to implement this without a breaking change, though it'll be ugly with some bits of code purely dedicated to backwards compatibility. We'd have to put a new registration mechanism with new names next to the current one, and turn the current one into a proxy:

    type FuncWithCustomError=func(fl FieldLevel) error
    func (v *Validate) RegisterValidationWithCustomError(tag string, fn FuncWithCustomError, callValidationEvenIfNull ...bool) error {
        // Current implementation but for FuncWithCustomError instead of Func.
    }
    
    type Func=func(fl FieldLevel) bool
    func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
        fnWithCustomError := func(fl FieldLevel) error {
            if fn(fl) {
                return nil
            } else {
                return errors.New("")
            }
        }
        return RegisterValidationWithCustomError(tag, fnWithCustomError , callValidationEvenIfNull...)
    }

    Of course, it would still be a pretty big change to the deeper architecture of the library: all baked-in validators would need to be wrapped, all code which calls the validators needs to call them by the new signature, and I don't know the codebase well enough to tell if more things than that need to be changed.

    Would the maintainer(s) of this project be on board with this solution? If yes, would you appreciate me familiarising myself with the codebase and cooking up a PR for this, or would you prefer to implement this yourself?

  10. ezk84 commented on Oct 30, 2024

    @ezk84

    Need this also.

    To reiterate, because some comments here seem to think this is about purely customising the error message, but it is not. It's about being able to return an error message that gives deeper reasons why a custom validation failed. That reason might change according to the intricate and particular contents of the field.

    Imagine for example a struct field that is a custom string type, and it is meant to conform to a particular grammar that I've created elsewhere in my code. What I want is to return specific, helpful error messages when that parsing fails. The message will change according to the specifics of the input string, so can't be solved with static custom message tags, or with the current "translation" workaround.

    +1 on @joostvdhoff's proposal from me!

  11. mbarnes commented on Mar 12, 2025

    @mbarnes

    Here's a limited v10 workaround for single field validation. It doesn't work for cross field or cross struct validations, but might work if you just need to, say, validate syntax on a string field. It does require executing the custom validation twice: once to induce a FieldError and again to get an actual error back when processing FieldErrors.

    This isn't production-ready code, but just to give the flavor.

    func RealValidateFoo(foo string, param string) error {
       // perform syntax validation on foo
    }
    
    err = validate.RegisterValidation("foo", func(fl validator.FieldLevel) bool {
            return RealValidateFoo(fl.Field().String(), fl.Param()) == nil
    })
    
    // later on, when iterating over validation errors
    
    switch err := err.(type) {
    case validator.ValidationErrors:
            for _, fieldErr := range err {
                    errMsg := fieldErr.Error()
    
                    switch fieldErr.Tag() {
                    case "foo":
                            customErr := RealValidateFoo(fmt.Sprint(fieldErr.Value()), fieldErr.Param())
                            if customErr != nil {
                                    errMsg = customErr.Error()
                            }
                    // check other custom validations
                    }
    
                    fmt.Println(errMsg)
            }
    ...
    }
    
    
  12. dumindu commented on Jun 25, 2025

    @dumindu

    Please also support forwarding parameters in aliases. For example,

    validate.RegisterAlias("minChar", "min")
    validate.RegisterAlias("maxChar", "max")

    So, can define different messages for the same validator.

  13. avasenin commented on Jul 7, 2025

    @avasenin

    i'm also interested on this. Currently was able to workaround this with

    func (v *Validator) validateExpr(ctx context.Context, fl validator.FieldLevel) bool {
    	expr := fl.Field().String()
    	_, err := doThing(ctx, expr)
    	if err != nil {
    		type reporter interface {
    			ReportError(field interface{}, fieldName, structFieldName string, tag, param string)
    		}
    		// 
    		if r, ok := fl.(reporter); ok {
    			r.ReportError(fl.Field(), fl.FieldName(), fl.StructFieldName(), fl.GetTag(), err.Error())
    			// we don't return false here as we use alternative way to report the error
    			return true
    		}
    		return false
    	}
    	return true
    }
    

    But the solution is hackish. Actually just an ability to set Param in validator.FieldLevel will be already good enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    ⌂ 换网址