12 ut "github.com/go-playground/universal-translator"
16 defaultTagName = "validate"
22 structOnlyTag = "structonly"
23 noStructLevelTag = "nostructlevel"
24 omitempty = "omitempty"
25 isdefault = "isdefault"
26 skipValidationTag = "-"
28 requiredTag = "required"
29 namespaceSeparator = "."
32 restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
33 restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
34 restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
38 timeType = reflect.TypeOf(time.Time{})
39 defaultCField = &cField{namesEqual: true}
42 // FilterFunc is the type used to filter fields using
43 // StructFiltered(...) function.
44 // returning true results in the field being filtered/skiped from
46 type FilterFunc func(ns []byte) bool
48 // CustomTypeFunc allows for overriding or adding custom field type handler functions
49 // field = field value of the type to return a value to be validated
50 // example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
51 type CustomTypeFunc func(field reflect.Value) interface{}
53 // TagNameFunc allows for adding of a custom tag name parser
54 type TagNameFunc func(field reflect.StructField) string
56 // Validate contains the validator settings and cache
57 type Validate struct {
62 tagNameFunc TagNameFunc
63 structLevelFuncs map[reflect.Type]StructLevelFuncCtx
64 customFuncs map[reflect.Type]CustomTypeFunc
65 aliases map[string]string
66 validations map[string]FuncCtx
67 transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
69 structCache *structCache
72 // New returns a new instacne of 'validate' with sane defaults.
73 func New() *Validate {
76 tc.m.Store(make(map[string]*cTag))
78 sc := new(structCache)
79 sc.m.Store(make(map[reflect.Type]*cStruct))
82 tagName: defaultTagName,
83 aliases: make(map[string]string, len(bakedInAliases)),
84 validations: make(map[string]FuncCtx, len(bakedInValidators)),
89 // must copy alias validators for separate validations to be used in each validator instance
90 for k, val := range bakedInAliases {
91 v.RegisterAlias(k, val)
94 // must copy validators for separate validations to be used in each instance
95 for k, val := range bakedInValidators {
97 // no need to error check here, baked in will always be valid
98 v.registerValidation(k, wrapFunc(val), true)
102 New: func() interface{} {
105 ns: make([]byte, 0, 64),
106 actualNs: make([]byte, 0, 64),
107 misc: make([]byte, 32),
115 // SetTagName allows for changing of the default tag name of 'validate'
116 func (v *Validate) SetTagName(name string) {
120 // RegisterTagNameFunc registers a function to get another name from the
121 // StructField eg. the JSON name
122 func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
124 v.hasTagNameFunc = true
127 // RegisterValidation adds a validation with the given tag
130 // - if the key already exists, the previous validation function will be replaced.
131 // - this method is not thread-safe it is intended that these all be registered prior to any validation
132 func (v *Validate) RegisterValidation(tag string, fn Func) error {
133 return v.RegisterValidationCtx(tag, wrapFunc(fn))
136 // RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
137 // allowing context.Context validation support.
138 func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx) error {
139 return v.registerValidation(tag, fn, false)
142 func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) error {
145 return errors.New("Function Key cannot be empty")
149 return errors.New("Function cannot be empty")
152 _, ok := restrictedTags[tag]
154 if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
155 panic(fmt.Sprintf(restrictedTagErr, tag))
158 v.validations[tag] = fn
163 // RegisterAlias registers a mapping of a single validation tag that
164 // defines a common or complex set of validation(s) to simplify adding validation
167 // NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
168 func (v *Validate) RegisterAlias(alias, tags string) {
170 _, ok := restrictedTags[alias]
172 if ok || strings.ContainsAny(alias, restrictedTagChars) {
173 panic(fmt.Sprintf(restrictedAliasErr, alias))
176 v.aliases[alias] = tags
179 // RegisterStructValidation registers a StructLevelFunc against a number of types.
182 // - this method is not thread-safe it is intended that these all be registered prior to any validation
183 func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
184 v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
187 // RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
188 // of contextual validation information via context.Context.
191 // - this method is not thread-safe it is intended that these all be registered prior to any validation
192 func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
194 if v.structLevelFuncs == nil {
195 v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
198 for _, t := range types {
199 v.structLevelFuncs[reflect.TypeOf(t)] = fn
203 // RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
205 // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
206 func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
208 if v.customFuncs == nil {
209 v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
212 for _, t := range types {
213 v.customFuncs[reflect.TypeOf(t)] = fn
216 v.hasCustomFuncs = true
219 // RegisterTranslation registers translations against the provided tag.
220 func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
222 if v.transTagFunc == nil {
223 v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
226 if err = registerFn(trans); err != nil {
230 m, ok := v.transTagFunc[trans]
232 m = make(map[string]TranslationFunc)
233 v.transTagFunc[trans] = m
236 m[tag] = translationFn
241 // Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
243 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
244 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
245 func (v *Validate) Struct(s interface{}) error {
246 return v.StructCtx(context.Background(), s)
249 // StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
250 // and also allows passing of context.Context for contextual validation information.
252 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
253 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
254 func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
256 val := reflect.ValueOf(s)
259 if val.Kind() == reflect.Ptr && !val.IsNil() {
263 if val.Kind() != reflect.Struct || val.Type() == timeType {
264 return &InvalidValidationError{Type: reflect.TypeOf(s)}
268 vd := v.pool.Get().(*validate)
271 // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
273 vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
275 if len(vd.errs) > 0 {
285 // StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
286 // nested structs, unless otherwise specified.
288 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
289 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
290 func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error {
291 return v.StructFilteredCtx(context.Background(), s, fn)
294 // StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
295 // nested structs, unless otherwise specified and also allows passing of contextual validation information via
298 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
299 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
300 func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) {
301 val := reflect.ValueOf(s)
304 if val.Kind() == reflect.Ptr && !val.IsNil() {
308 if val.Kind() != reflect.Struct || val.Type() == timeType {
309 return &InvalidValidationError{Type: reflect.TypeOf(s)}
313 vd := v.pool.Get().(*validate)
317 // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
319 vd.validateStruct(context.Background(), top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
321 if len(vd.errs) > 0 {
331 // StructPartial validates the fields passed in only, ignoring all others.
332 // Fields may be provided in a namespaced fashion relative to the struct provided
333 // eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
335 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
336 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
337 func (v *Validate) StructPartial(s interface{}, fields ...string) error {
338 return v.StructPartialCtx(context.Background(), s, fields...)
341 // StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
342 // validation validation information via context.Context
343 // Fields may be provided in a namespaced fashion relative to the struct provided
344 // eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
346 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
347 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
348 func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
349 val := reflect.ValueOf(s)
352 if val.Kind() == reflect.Ptr && !val.IsNil() {
356 if val.Kind() != reflect.Struct || val.Type() == timeType {
357 return &InvalidValidationError{Type: reflect.TypeOf(s)}
361 vd := v.pool.Get().(*validate)
365 vd.hasExcludes = false
366 vd.includeExclude = make(map[string]struct{})
372 for _, k := range fields {
374 flds := strings.Split(k, namespaceSeparator)
377 vd.misc = append(vd.misc[0:0], name...)
378 vd.misc = append(vd.misc, '.')
380 for _, s := range flds {
382 idx := strings.Index(s, leftBracket)
386 vd.misc = append(vd.misc, s[:idx]...)
387 vd.includeExclude[string(vd.misc)] = struct{}{}
389 idx2 := strings.Index(s, rightBracket)
391 vd.misc = append(vd.misc, s[idx:idx2]...)
392 vd.includeExclude[string(vd.misc)] = struct{}{}
394 idx = strings.Index(s, leftBracket)
398 vd.misc = append(vd.misc, s...)
399 vd.includeExclude[string(vd.misc)] = struct{}{}
402 vd.misc = append(vd.misc, '.')
408 vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
410 if len(vd.errs) > 0 {
420 // StructExcept validates all fields except the ones passed in.
421 // Fields may be provided in a namespaced fashion relative to the struct provided
422 // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
424 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
425 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
426 func (v *Validate) StructExcept(s interface{}, fields ...string) error {
427 return v.StructExceptCtx(context.Background(), s, fields...)
430 // StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
431 // validation validation information via context.Context
432 // Fields may be provided in a namespaced fashion relative to the struct provided
433 // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
435 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
436 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
437 func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
438 val := reflect.ValueOf(s)
441 if val.Kind() == reflect.Ptr && !val.IsNil() {
445 if val.Kind() != reflect.Struct || val.Type() == timeType {
446 return &InvalidValidationError{Type: reflect.TypeOf(s)}
450 vd := v.pool.Get().(*validate)
454 vd.hasExcludes = true
455 vd.includeExclude = make(map[string]struct{})
460 for _, key := range fields {
462 vd.misc = vd.misc[0:0]
465 vd.misc = append(vd.misc, name...)
466 vd.misc = append(vd.misc, '.')
469 vd.misc = append(vd.misc, key...)
470 vd.includeExclude[string(vd.misc)] = struct{}{}
473 vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
475 if len(vd.errs) > 0 {
485 // Var validates a single variable using tag style validation.
488 // validate.Var(i, "gt=1,lt=10")
490 // WARNING: a struct can be passed for validation eg. time.Time is a struct or
491 // if you have a custom type and have registered a custom type handler, so must
492 // allow it; however unforseen validations will occur if trying to validate a
493 // struct that is meant to be passed to 'validate.Struct'
495 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
496 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
497 // validate Array, Slice and maps fields which may contain more than one error
498 func (v *Validate) Var(field interface{}, tag string) error {
499 return v.VarCtx(context.Background(), field, tag)
502 // VarCtx validates a single variable using tag style validation and allows passing of contextual
503 // validation validation information via context.Context.
506 // validate.Var(i, "gt=1,lt=10")
508 // WARNING: a struct can be passed for validation eg. time.Time is a struct or
509 // if you have a custom type and have registered a custom type handler, so must
510 // allow it; however unforseen validations will occur if trying to validate a
511 // struct that is meant to be passed to 'validate.Struct'
513 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
514 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
515 // validate Array, Slice and maps fields which may contain more than one error
516 func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) {
517 if len(tag) == 0 || tag == skipValidationTag {
522 ctag, ok := v.tagCache.Get(tag)
524 v.tagCache.lock.Lock()
525 defer v.tagCache.lock.Unlock()
527 // could have been multiple trying to access, but once first is done this ensures tag
528 // isn't parsed again.
529 ctag, ok = v.tagCache.Get(tag)
531 ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false)
532 v.tagCache.Set(tag, ctag)
536 val := reflect.ValueOf(field)
538 vd := v.pool.Get().(*validate)
542 vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
544 if len(vd.errs) > 0 {
554 // VarWithValue validates a single variable, against another variable/field's value using tag style validation
558 // validate.VarWithValue(s1, s2, "eqcsfield") // returns true
560 // WARNING: a struct can be passed for validation eg. time.Time is a struct or
561 // if you have a custom type and have registered a custom type handler, so must
562 // allow it; however unforseen validations will occur if trying to validate a
563 // struct that is meant to be passed to 'validate.Struct'
565 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
566 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
567 // validate Array, Slice and maps fields which may contain more than one error
568 func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error {
569 return v.VarWithValueCtx(context.Background(), field, other, tag)
572 // VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
573 // allows passing of contextual validation validation information via context.Context.
577 // validate.VarWithValue(s1, s2, "eqcsfield") // returns true
579 // WARNING: a struct can be passed for validation eg. time.Time is a struct or
580 // if you have a custom type and have registered a custom type handler, so must
581 // allow it; however unforseen validations will occur if trying to validate a
582 // struct that is meant to be passed to 'validate.Struct'
584 // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
585 // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
586 // validate Array, Slice and maps fields which may contain more than one error
587 func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) {
588 if len(tag) == 0 || tag == skipValidationTag {
593 ctag, ok := v.tagCache.Get(tag)
595 v.tagCache.lock.Lock()
596 defer v.tagCache.lock.Unlock()
598 // could have been multiple trying to access, but once first is done this ensures tag
599 // isn't parsed again.
600 ctag, ok = v.tagCache.Get(tag)
602 ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false)
603 v.tagCache.Set(tag, ctag)
607 otherVal := reflect.ValueOf(other)
609 vd := v.pool.Get().(*validate)
613 vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
615 if len(vd.errs) > 0 {