validator
validator copied to clipboard
Custom error message
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 {
}
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
@1379 sounds like a great enhancement for the next version of the lib, but unfortunately right now is a breaking change.
@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.
Same need
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
here is a way to fix it #837
same need. especially for sl.ReportError() on struct validation to include custom error messages to user
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?
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!
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)
}
...
}
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.
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.