[ADD] slice/array binding support

This commit is contained in:
wuhuizuo 2019-12-25 09:24:20 +00:00
parent b9c96709c0
commit 13ea8367dc
3 changed files with 87 additions and 15 deletions

View File

@ -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)
}

View File

@ -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())

View File

@ -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,23 +20,64 @@ 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
}
}
// 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
// Validator instance. This is useful if you want to register custom validations