mirror of https://github.com/gin-gonic/gin.git
Merge branch 'master' into http-abort-err
This commit is contained in:
commit
1fc5ec954c
28
.travis.yml
28
.travis.yml
|
@ -18,6 +18,34 @@ matrix:
|
|||
env:
|
||||
- TESTTAGS=nomsgpack
|
||||
- go: master
|
||||
# Adding ppc64le jobs
|
||||
- go: 1.11.x
|
||||
arch: ppc64le
|
||||
env: GO111MODULE=on
|
||||
- go: 1.12.x
|
||||
arch: ppc64le
|
||||
env: GO111MODULE=on
|
||||
- go: 1.13.x
|
||||
arch: ppc64le
|
||||
- go: 1.13.x
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TESTTAGS=nomsgpack
|
||||
- go: 1.14.x
|
||||
arch: ppc64le
|
||||
- go: 1.14.x
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TESTTAGS=nomsgpack
|
||||
- go: 1.15.x
|
||||
arch: ppc64le
|
||||
- go: 1.15.x
|
||||
arch: ppc64le
|
||||
env:
|
||||
- TESTTAGS=nomsgpack
|
||||
- go: master
|
||||
arch: ppc64le
|
||||
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
|
|
@ -51,7 +51,8 @@ type BindingUri interface {
|
|||
// 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.
|
||||
// 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, the validation should be performed travel 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 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.
|
||||
|
|
|
@ -35,7 +35,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 {
|
||||
|
@ -181,6 +181,20 @@ 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", "/", "/", `[{"foo": "123"}]`, `[{}]`)
|
||||
testBodyBindingSlice(t, JSON, "json", "/", "/", `[{"foo": "123"}]`, `[{"foo": ""}]`)
|
||||
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",
|
||||
|
@ -1181,6 +1195,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 testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
|
||||
obj := make(map[string]string)
|
||||
req := requestWithBody("POST", path, body)
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
@ -16,24 +18,56 @@ type defaultValidator struct {
|
|||
validate *validator.Validate
|
||||
}
|
||||
|
||||
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, e.Error()))
|
||||
}
|
||||
return strings.Join(errMsgs, "\n")
|
||||
}
|
||||
|
||||
var _ StructValidator = &defaultValidator{}
|
||||
|
||||
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||
value := reflect.ValueOf(obj)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
if valueType == reflect.Struct {
|
||||
v.lazyinit()
|
||||
if err := v.validate.Struct(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(obj)
|
||||
switch value.Kind() {
|
||||
case reflect.Ptr:
|
||||
return v.ValidateStruct(value.Elem().Interface())
|
||||
case reflect.Struct:
|
||||
return v.validateStruct(obj)
|
||||
case reflect.Slice, reflect.Array:
|
||||
count := value.Len()
|
||||
validateRet := make(sliceValidateError, 0)
|
||||
for i := 0; i < count; i++ {
|
||||
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
|
||||
validateRet = append(validateRet, err)
|
||||
}
|
||||
}
|
||||
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
|
||||
// or struct level validations. See validator GoDoc for more info -
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2020 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSliceValidateError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err sliceValidateError
|
||||
want string
|
||||
}{
|
||||
{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.err.Error(); got != tt.want {
|
||||
t.Errorf("sliceValidateError.Error() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultValidator(t *testing.T) {
|
||||
type exampleStruct struct {
|
||||
A string `binding:"max=8"`
|
||||
B int `binding:"gt=0"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v *defaultValidator
|
||||
obj interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{"validate nil obj", &defaultValidator{}, nil, false},
|
||||
{"validate int obj", &defaultValidator{}, 3, false},
|
||||
{"validate struct failed-1", &defaultValidator{}, exampleStruct{A: "123456789", B: 1}, true},
|
||||
{"validate struct failed-2", &defaultValidator{}, exampleStruct{A: "12345678", B: 0}, true},
|
||||
{"validate struct passed", &defaultValidator{}, exampleStruct{A: "12345678", B: 1}, false},
|
||||
{"validate *struct failed-1", &defaultValidator{}, &exampleStruct{A: "123456789", B: 1}, true},
|
||||
{"validate *struct failed-2", &defaultValidator{}, &exampleStruct{A: "12345678", B: 0}, true},
|
||||
{"validate *struct passed", &defaultValidator{}, &exampleStruct{A: "12345678", B: 1}, false},
|
||||
{"validate []struct failed-1", &defaultValidator{}, []exampleStruct{{A: "123456789", B: 1}}, true},
|
||||
{"validate []struct failed-2", &defaultValidator{}, []exampleStruct{{A: "12345678", B: 0}}, true},
|
||||
{"validate []struct passed", &defaultValidator{}, []exampleStruct{{A: "12345678", B: 1}}, false},
|
||||
{"validate []*struct failed-1", &defaultValidator{}, []*exampleStruct{{A: "123456789", B: 1}}, true},
|
||||
{"validate []*struct failed-2", &defaultValidator{}, []*exampleStruct{{A: "12345678", B: 0}}, true},
|
||||
{"validate []*struct passed", &defaultValidator{}, []*exampleStruct{{A: "12345678", B: 1}}, false},
|
||||
{"validate *[]struct failed-1", &defaultValidator{}, &[]exampleStruct{{A: "123456789", B: 1}}, true},
|
||||
{"validate *[]struct failed-2", &defaultValidator{}, &[]exampleStruct{{A: "12345678", B: 0}}, true},
|
||||
{"validate *[]struct passed", &defaultValidator{}, &[]exampleStruct{{A: "12345678", B: 1}}, false},
|
||||
{"validate *[]*struct failed-1", &defaultValidator{}, &[]*exampleStruct{{A: "123456789", B: 1}}, true},
|
||||
{"validate *[]*struct failed-2", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 0}}, true},
|
||||
{"validate *[]*struct passed", &defaultValidator{}, &[]*exampleStruct{{A: "12345678", B: 1}}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.v.ValidateStruct(tt.obj); (err != nil) != tt.wantErr {
|
||||
t.Errorf("defaultValidator.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -5,16 +5,17 @@
|
|||
package bytesconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// StringToBytes converts string to byte slice without a memory allocation.
|
||||
func StringToBytes(s string) (b []byte) {
|
||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
||||
return b
|
||||
return *(*[]byte)(unsafe.Pointer(
|
||||
&struct {
|
||||
string
|
||||
Cap int
|
||||
}{s, len(s)},
|
||||
))
|
||||
}
|
||||
|
||||
// BytesToString converts byte slice to string without a memory allocation.
|
||||
|
|
53
tree.go
53
tree.go
|
@ -119,7 +119,6 @@ func (n *node) incrementChildPrio(pos int) int {
|
|||
for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
|
||||
// Swap node positions
|
||||
cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
|
||||
|
||||
}
|
||||
|
||||
// Build new index char string
|
||||
|
@ -559,8 +558,8 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]by
|
|||
// Use a static sized buffer on the stack in the common case.
|
||||
// If the path is too long, allocate a buffer on the heap instead.
|
||||
buf := make([]byte, 0, stackBufSize)
|
||||
if l := len(path) + 1; l > stackBufSize {
|
||||
buf = make([]byte, 0, l)
|
||||
if length := len(path) + 1; length > stackBufSize {
|
||||
buf = make([]byte, 0, length)
|
||||
}
|
||||
|
||||
ciPath := n.findCaseInsensitivePathRec(
|
||||
|
@ -600,7 +599,30 @@ walk: // Outer loop for walking the tree
|
|||
path = path[npLen:]
|
||||
ciPath = append(ciPath, n.path...)
|
||||
|
||||
if len(path) > 0 {
|
||||
if len(path) == 0 {
|
||||
// We should have reached the node containing the handle.
|
||||
// Check if this node has a handle registered.
|
||||
if n.handlers != nil {
|
||||
return ciPath
|
||||
}
|
||||
|
||||
// No handle found.
|
||||
// Try to fix the path by adding a trailing slash
|
||||
if fixTrailingSlash {
|
||||
for i, c := range []byte(n.indices) {
|
||||
if c == '/' {
|
||||
n = n.children[i]
|
||||
if (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil) {
|
||||
return append(ciPath, '/')
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this node does not have a wildcard (param or catchAll) child,
|
||||
// we can just look up the next child node and continue to walk down
|
||||
// the tree
|
||||
|
@ -735,29 +757,6 @@ walk: // Outer loop for walking the tree
|
|||
default:
|
||||
panic("invalid node type")
|
||||
}
|
||||
} else {
|
||||
// We should have reached the node containing the handle.
|
||||
// Check if this node has a handle registered.
|
||||
if n.handlers != nil {
|
||||
return ciPath
|
||||
}
|
||||
|
||||
// No handle found.
|
||||
// Try to fix the path by adding a trailing slash
|
||||
if fixTrailingSlash {
|
||||
for i, c := range []byte(n.indices) {
|
||||
if c == '/' {
|
||||
n = n.children[i]
|
||||
if (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil) {
|
||||
return append(ciPath, '/')
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found.
|
||||
|
|
Loading…
Reference in New Issue