mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 14:52:18 +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
|
||||
}
|
||||
|
||||
// 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
|
||||
// of the request. Gin provides a default implementation for this using
|
||||
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||
type StructValidator interface {
|
||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||
type ValidatorImp interface {
|
||||
// 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 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.
|
||||
// Otherwise nil must be returned.
|
||||
ValidateStruct(interface{}) error
|
||||
Validate(interface{}) error
|
||||
|
||||
// Engine returns the underlying validator engine which powers the
|
||||
// StructValidator implementation.
|
||||
@ -65,7 +65,7 @@ type StructValidator interface {
|
||||
// Validator is the default validator which implements the StructValidator
|
||||
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
||||
// under the hood.
|
||||
var Validator StructValidator = &defaultValidator{}
|
||||
var Validator ValidatorImp = &defaultValidator{}
|
||||
|
||||
// These implement the Binding interface and can be used to bind the data
|
||||
// present in the request to struct instances.
|
||||
@ -112,5 +112,5 @@ func validate(obj interface{}) error {
|
||||
if Validator == nil {
|
||||
return nil
|
||||
}
|
||||
return Validator.ValidateStruct(obj)
|
||||
return Validator.Validate(obj)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ type QueryTest 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 {
|
||||
@ -180,6 +180,19 @@ func TestBindingJSON(t *testing.T) {
|
||||
`{"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) {
|
||||
testBodyBindingUseNumber(t,
|
||||
JSON, "json",
|
||||
@ -1114,6 +1127,20 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
|
||||
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) {
|
||||
assert.Equal(t, name, b.Name())
|
||||
|
||||
|
@ -6,9 +6,13 @@ package binding
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"fmt"
|
||||
"sync"
|
||||
"strings"
|
||||
"log"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
// "github.com/ahmetb/go-linq"
|
||||
)
|
||||
|
||||
type defaultValidator struct {
|
||||
@ -16,22 +20,63 @@ type defaultValidator struct {
|
||||
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.
|
||||
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)
|
||||
valueType := value.Kind()
|
||||
log.Printf("valueType: %v, %#v\n", valueType, valueType)
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
value = value.Elem()
|
||||
valueType = value.Kind()
|
||||
}
|
||||
if valueType == reflect.Struct {
|
||||
v.lazyinit()
|
||||
if err := v.validate.Struct(obj); err != nil {
|
||||
return err
|
||||
|
||||
switch valueType {
|
||||
case reflect.Struct:
|
||||
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 validateRet
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user