mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 05:42:09 +08:00
Add support for validating map types
This commit is contained in:
parent
cb9b68b1b5
commit
04ccf172a6
@ -52,9 +52,9 @@ type BindingUri interface {
|
|||||||
// https://github.com/go-playground/validator/tree/v10.6.1.
|
// https://github.com/go-playground/validator/tree/v10.6.1.
|
||||||
type StructValidator interface {
|
type StructValidator interface {
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
// If the received type is a slice|array, the validation should be performed travel on every element.
|
// If the received type is a slice/array/map, the validation should be performed on every element.
|
||||||
// If the received type is not a struct or slice|array, any validation should be skipped and nil must be returned.
|
// If the received type is not a struct or slice/array/map, any validation should be skipped and nil must be returned.
|
||||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
// If the received type is a pointer to a struct/slice/array/map, the validation should be performed.
|
||||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||||
// Otherwise nil must be returned.
|
// Otherwise nil must be returned.
|
||||||
ValidateStruct(interface{}) error
|
ValidateStruct(interface{}) error
|
||||||
|
@ -50,8 +50,9 @@ type BindingUri interface {
|
|||||||
// https://github.com/go-playground/validator/tree/v10.6.1.
|
// https://github.com/go-playground/validator/tree/v10.6.1.
|
||||||
type StructValidator interface {
|
type StructValidator interface {
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
// If the received type is a slice/array/map, the validation should be performed on every element.
|
||||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
// If the received type is not a struct or slice/array/map, any validation should be skipped and nil must be returned.
|
||||||
|
// If the received type is a pointer to a struct/slice/array/map, the validation should be performed.
|
||||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||||
// Otherwise nil must be returned.
|
// Otherwise nil must be returned.
|
||||||
ValidateStruct(interface{}) error
|
ValidateStruct(interface{}) error
|
||||||
|
@ -41,9 +41,33 @@ func (fe sliceFieldError) Unwrap() error {
|
|||||||
return fe.FieldError
|
return fe.FieldError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapFieldError is returned for invalid map values.
|
||||||
|
// It extends validator.FieldError with the key of the failing value.
|
||||||
|
type MapFieldError interface {
|
||||||
|
validator.FieldError
|
||||||
|
Key() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapFieldError struct {
|
||||||
|
validator.FieldError
|
||||||
|
key interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fe mapFieldError) Key() interface{} {
|
||||||
|
return fe.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fe mapFieldError) Error() string {
|
||||||
|
return fmt.Sprintf("[%v]: %s", fe.key, fe.FieldError.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fe mapFieldError) Unwrap() error {
|
||||||
|
return fe.FieldError
|
||||||
|
}
|
||||||
|
|
||||||
var _ StructValidator = &defaultValidator{}
|
var _ StructValidator = &defaultValidator{}
|
||||||
|
|
||||||
// ValidateStruct receives any kind of type, but validates only structs, pointers, slices, and arrays.
|
// ValidateStruct receives any kind of type, but validates only structs, pointers, slices, arrays, and maps.
|
||||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -56,8 +80,8 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
|||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return v.validateStruct(obj)
|
return v.validateStruct(obj)
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
count := value.Len()
|
|
||||||
var errs validator.ValidationErrors
|
var errs validator.ValidationErrors
|
||||||
|
count := value.Len()
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
|
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
|
||||||
for _, fieldError := range err.(validator.ValidationErrors) { // nolint: errorlint
|
for _, fieldError := range err.(validator.ValidationErrors) { // nolint: errorlint
|
||||||
@ -69,6 +93,19 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case reflect.Map:
|
||||||
|
var errs validator.ValidationErrors
|
||||||
|
for _, key := range value.MapKeys() {
|
||||||
|
if err := v.ValidateStruct(value.MapIndex(key).Interface()); err != nil {
|
||||||
|
for _, fieldError := range err.(validator.ValidationErrors) { // nolint: errorlint
|
||||||
|
errs = append(errs, mapFieldError{fieldError, key.Interface()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,20 @@ func TestSliceFieldError(t *testing.T) {
|
|||||||
assert.Equal(t, fe, errors.Unwrap(err))
|
assert.Equal(t, fe, errors.Unwrap(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapFieldError(t *testing.T) {
|
||||||
|
var fe validator.FieldError = dummyFieldError{msg: "test error"}
|
||||||
|
|
||||||
|
var err MapFieldError = mapFieldError{fe, "test key"}
|
||||||
|
assert.Equal(t, "test key", err.Key())
|
||||||
|
assert.Equal(t, "[test key]: test error", err.Error())
|
||||||
|
assert.Equal(t, fe, errors.Unwrap(err))
|
||||||
|
|
||||||
|
err = mapFieldError{fe, 123}
|
||||||
|
assert.Equal(t, 123, err.Key())
|
||||||
|
assert.Equal(t, "[123]: test error", err.Error())
|
||||||
|
assert.Equal(t, fe, errors.Unwrap(err))
|
||||||
|
}
|
||||||
|
|
||||||
type dummyFieldError struct {
|
type dummyFieldError struct {
|
||||||
validator.FieldError
|
validator.FieldError
|
||||||
msg string
|
msg string
|
||||||
@ -61,6 +75,18 @@ func TestDefaultValidator(t *testing.T) {
|
|||||||
{"validate *[]*struct failed-1", &defaultValidator{}, &[]*exampleStruct{{A: "123456789", B: 1}}, true},
|
{"validate *[]*struct failed-1", &defaultValidator{}, &[]*exampleStruct{{A: "123456789", B: 1}}, true},
|
||||||
{"validate *[]*struct failed-2", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 0}}, true},
|
{"validate *[]*struct failed-2", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 0}}, true},
|
||||||
{"validate *[]*struct passed", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 1}}, false},
|
{"validate *[]*struct passed", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 1}}, false},
|
||||||
|
{"validate map[string]struct failed-1", &defaultValidator{}, map[string]exampleStruct{"x": {A: "123456789", B: 1}}, true},
|
||||||
|
{"validate map[string]struct failed-2", &defaultValidator{}, map[string]exampleStruct{"x": {A: "12345678", B: 0}}, true},
|
||||||
|
{"validate map[string]struct passed", &defaultValidator{}, map[string]exampleStruct{"x": {A: "12345678", B: 1}}, false},
|
||||||
|
{"validate map[string]*struct failed-1", &defaultValidator{}, map[string]*exampleStruct{"x": {A: "123456789", B: 1}}, true},
|
||||||
|
{"validate map[string]*struct failed-2", &defaultValidator{}, map[string]*exampleStruct{"x": {A: "12345678", B: 0}}, true},
|
||||||
|
{"validate map[string]*struct passed", &defaultValidator{}, map[string]*exampleStruct{"x": {A: "12345678", B: 1}}, false},
|
||||||
|
{"validate *map[string]struct failed-1", &defaultValidator{}, &map[string]exampleStruct{"x": {A: "123456789", B: 1}}, true},
|
||||||
|
{"validate *map[string]struct failed-2", &defaultValidator{}, &map[string]exampleStruct{"x": {A: "12345678", B: 0}}, true},
|
||||||
|
{"validate *map[string]struct passed", &defaultValidator{}, &map[string]exampleStruct{"x": {A: "12345678", B: 1}}, false},
|
||||||
|
{"validate *map[string]*struct failed-1", &defaultValidator{}, &map[string]*exampleStruct{"x": {A: "123456789", B: 1}}, true},
|
||||||
|
{"validate *map[string]*struct failed-2", &defaultValidator{}, &map[string]*exampleStruct{"x": {A: "12345678", B: 0}}, true},
|
||||||
|
{"validate *map[string]*struct passed", &defaultValidator{}, &map[string]*exampleStruct{"x": {A: "12345678", B: 1}}, false},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user