From 0b0f695dc5b82b7150248920dce518c3cd68f4fc Mon Sep 17 00:00:00 2001 From: qm012 Date: Wed, 23 Jun 2021 10:15:09 +0800 Subject: [PATCH] support custom message --- .gitignore | 1 + cache.go | 11 +++++++++++ errors.go | 4 ++++ validator.go | 6 ++++++ validator_instance.go | 26 +++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6e43fac..42950c7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,6 @@ _testmain.go *.test *.out *.txt +.idea cover.html README.html diff --git a/cache.go b/cache.go index 0d18d6e..314beda 100644 --- a/cache.go +++ b/cache.go @@ -79,6 +79,7 @@ type cField struct { name string altName string namesEqual bool + tagValues map[string]string // custom multiple tag information tagname:tagvalue cTags *cTag } @@ -154,11 +155,21 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr ctag = new(cTag) } + // handle custom tag map + length := len(v.customTags) + tagValues := make(map[string]string, length) + + for i := 0; i < length; i++ { + ctname := v.customTags[i] + tagValues[ctname] = strings.TrimSpace(fld.Tag.Get(ctname)) + } + cs.fields = append(cs.fields, &cField{ idx: i, name: fld.Name, altName: customName, cTags: ctag, + tagValues: tagValues, namesEqual: fld.Name == customName, }) } diff --git a/errors.go b/errors.go index 9a1b1ab..124f775 100644 --- a/errors.go +++ b/errors.go @@ -177,6 +177,7 @@ type fieldError struct { structfieldLen uint8 value interface{} param string + cem string // custom error message kind reflect.Kind typ reflect.Type } @@ -251,6 +252,9 @@ func (fe *fieldError) Type() reflect.Type { // Error returns the fieldError's error message func (fe *fieldError) Error() string { + if len(fe.cem) != 0 { + return fe.cem + } return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag) } diff --git a/validator.go b/validator.go index 9569c0d..eb1c305 100644 --- a/validator.go +++ b/validator.go @@ -128,6 +128,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr fieldLen: uint8(len(cf.altName)), structfieldLen: uint8(len(cf.name)), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, }, ) @@ -152,6 +153,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr structfieldLen: uint8(len(cf.name)), value: current.Interface(), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, typ: current.Type(), }, @@ -197,6 +199,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr structfieldLen: uint8(len(cf.name)), value: current.Interface(), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, typ: typ, }, @@ -402,6 +405,7 @@ OUTER: structfieldLen: uint8(len(cf.name)), value: current.Interface(), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, typ: typ, }, @@ -422,6 +426,7 @@ OUTER: structfieldLen: uint8(len(cf.name)), value: current.Interface(), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, typ: typ, }, @@ -463,6 +468,7 @@ OUTER: structfieldLen: uint8(len(cf.name)), value: current.Interface(), param: ct.param, + cem: cf.tagValues[defaultErrTagName], kind: kind, typ: typ, }, diff --git a/validator_instance.go b/validator_instance.go index 8e27707..210433e 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -50,9 +50,22 @@ var ( timeDurationType = reflect.TypeOf(time.Duration(0)) timeType = reflect.TypeOf(time.Time{}) - defaultCField = &cField{namesEqual: true} + defaultCField = &cField{namesEqual: true} + defaultErrTagName = "message" ) +// StringSlice is custom tag slice data +type StringSlice []string + +func (s StringSlice) Position(name string) int { + for i := 0; i < len(s); i++ { + if s[i] == name { + return i + } + } + return -1 +} + // FilterFunc is the type used to filter fields using // StructFiltered(...) function. // returning true results in the field being filtered/skiped from @@ -75,6 +88,7 @@ type internalValidationFuncWrapper struct { // Validate contains the validator settings and cache type Validate struct { tagName string + customTags StringSlice pool *sync.Pool hasCustomFuncs bool hasTagNameFunc bool @@ -99,6 +113,7 @@ func New() *Validate { v := &Validate{ tagName: defaultTagName, + customTags: StringSlice{defaultErrTagName}, aliases: make(map[string]string, len(bakedInAliases)), validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), tagCache: tc, @@ -143,6 +158,15 @@ func (v *Validate) SetTagName(name string) { v.tagName = name } +// SetErrTagName allows for changing of the default tag name of 'message' +func (v *Validate) SetErrTagName(name string) { + index := v.customTags.Position(defaultErrTagName) + if index != -1 { + v.customTags[index] = name + } + defaultErrTagName = name +} + // ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual // validation validation information via context.Context. func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {