mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 23:12:17 +08:00
[ADD] slice/array binding support
This commit is contained in:
parent
b9c96709c0
commit
13ea8367dc
@ -45,17 +45,17 @@ type BindingUri interface {
|
|||||||
BindUri(map[string][]string, interface{}) error
|
BindUri(map[string][]string, interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructValidator is the minimal interface which needs to be implemented in
|
// ValidatorImp is the minimal interface which needs to be implemented in
|
||||||
// order for it to be used as the validator engine for ensuring the correctness
|
// order for it to be used as the validator engine for ensuring the correctness
|
||||||
// of the request. Gin provides a default implementation for this using
|
// of the request. Gin provides a default implementation for this using
|
||||||
// https://github.com/go-playground/validator/tree/v8.18.2.
|
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||||
type StructValidator interface {
|
type ValidatorImp interface {
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
// Validate 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 not a struct, 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 struct or pointer to a struct, 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
|
Validate(interface{}) error
|
||||||
|
|
||||||
// Engine returns the underlying validator engine which powers the
|
// Engine returns the underlying validator engine which powers the
|
||||||
// StructValidator implementation.
|
// StructValidator implementation.
|
||||||
@ -65,7 +65,7 @@ type StructValidator interface {
|
|||||||
// Validator is the default validator which implements the StructValidator
|
// Validator is the default validator which implements the StructValidator
|
||||||
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
||||||
// under the hood.
|
// under the hood.
|
||||||
var Validator StructValidator = &defaultValidator{}
|
var Validator ValidatorImp = &defaultValidator{}
|
||||||
|
|
||||||
// These implement the Binding interface and can be used to bind the data
|
// These implement the Binding interface and can be used to bind the data
|
||||||
// present in the request to struct instances.
|
// present in the request to struct instances.
|
||||||
@ -112,5 +112,5 @@ func validate(obj interface{}) error {
|
|||||||
if Validator == nil {
|
if Validator == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return Validator.ValidateStruct(obj)
|
return Validator.Validate(obj)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ type QueryTest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FooStruct struct {
|
type FooStruct struct {
|
||||||
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required"`
|
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required,max=32"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooBarStruct struct {
|
type FooBarStruct struct {
|
||||||
@ -180,6 +180,19 @@ func TestBindingJSON(t *testing.T) {
|
|||||||
`{"foo": "bar"}`, `{"bar": "foo"}`)
|
`{"foo": "bar"}`, `{"bar": "foo"}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindingJSONSlice(t *testing.T) {
|
||||||
|
EnableDecoderDisallowUnknownFields = true
|
||||||
|
defer func() {
|
||||||
|
EnableDecoderDisallowUnknownFields = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[]`, ``)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{}]`, `{}`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": 123}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"bar": 123}]`)
|
||||||
|
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": "123456789012345678901234567890123"}]`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBindingJSONUseNumber(t *testing.T) {
|
func TestBindingJSONUseNumber(t *testing.T) {
|
||||||
testBodyBindingUseNumber(t,
|
testBodyBindingUseNumber(t,
|
||||||
JSON, "json",
|
JSON, "json",
|
||||||
@ -1114,6 +1127,20 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
|
assert.Equal(t, name, b.Name())
|
||||||
|
|
||||||
|
var obj1 []FooStruct
|
||||||
|
req := requestWithBody("POST", path, body)
|
||||||
|
err := b.Bind(req, &obj1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var obj2 []FooStruct
|
||||||
|
req = requestWithBody("POST", badPath, badBody)
|
||||||
|
err = JSON.Bind(req, &obj2)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
assert.Equal(t, name, b.Name())
|
assert.Equal(t, name, b.Name())
|
||||||
|
|
||||||
|
@ -6,9 +6,13 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"strings"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
|
// "github.com/ahmetb/go-linq"
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultValidator struct {
|
type defaultValidator struct {
|
||||||
@ -16,23 +20,64 @@ type defaultValidator struct {
|
|||||||
validate *validator.Validate
|
validate *validator.Validate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ StructValidator = &defaultValidator{}
|
type sliceValidateError []error
|
||||||
|
|
||||||
|
func (err sliceValidateError) Error() string {
|
||||||
|
var errMsgs []string
|
||||||
|
for i, e := range err {
|
||||||
|
if e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, err.Error()))
|
||||||
|
}
|
||||||
|
return strings.Join(errMsgs, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ValidatorImp = &defaultValidator{}
|
||||||
|
|
||||||
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
||||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
func (v *defaultValidator) Validate(obj interface{}) error {
|
||||||
|
log.Println("called")
|
||||||
|
if obj == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
value := reflect.ValueOf(obj)
|
value := reflect.ValueOf(obj)
|
||||||
valueType := value.Kind()
|
valueType := value.Kind()
|
||||||
|
log.Printf("valueType: %v, %#v\n", valueType, valueType)
|
||||||
if valueType == reflect.Ptr {
|
if valueType == reflect.Ptr {
|
||||||
valueType = value.Elem().Kind()
|
value = value.Elem()
|
||||||
|
valueType = value.Kind()
|
||||||
}
|
}
|
||||||
if valueType == reflect.Struct {
|
|
||||||
v.lazyinit()
|
switch valueType {
|
||||||
if err := v.validate.Struct(obj); err != nil {
|
case reflect.Struct:
|
||||||
return err
|
log.Printf("goto validateStruct: %v, %#v\n", obj, obj)
|
||||||
|
return v.validateStruct(obj)
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
count := value.Len()
|
||||||
|
validateRet := make(sliceValidateError, 0)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
log.Println("called inside slice")
|
||||||
|
if err := v.Validate(value.Index(i)); err != nil {
|
||||||
|
validateRet = append(validateRet, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Println(validateRet)
|
||||||
|
if len(validateRet) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return validateRet
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateStruct receives struct type
|
||||||
|
func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||||
|
v.lazyinit()
|
||||||
|
return v.validate.Struct(obj)
|
||||||
|
}
|
||||||
|
|
||||||
// Engine returns the underlying validator engine which powers the default
|
// Engine returns the underlying validator engine which powers the default
|
||||||
// Validator instance. This is useful if you want to register custom validations
|
// Validator instance. This is useful if you want to register custom validations
|
||||||
|
Loading…
x
Reference in New Issue
Block a user