mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 14:12:16 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0132a404e0
55
README.md
55
README.md
@ -2021,6 +2021,61 @@ enough to call binding at once.
|
|||||||
can be called by `c.ShouldBind()` multiple times without any damage to
|
can be called by `c.ShouldBind()` multiple times without any damage to
|
||||||
performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)).
|
performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)).
|
||||||
|
|
||||||
|
### Bind form-data request with custom struct and custom tag
|
||||||
|
|
||||||
|
```go
|
||||||
|
const (
|
||||||
|
customerTag = "url"
|
||||||
|
defaultMemory = 32 << 20
|
||||||
|
)
|
||||||
|
|
||||||
|
type customerBinding struct {}
|
||||||
|
|
||||||
|
func (customerBinding) Name() string {
|
||||||
|
return "form"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (customerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
||||||
|
if err != http.ErrNotMultipart {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validate(obj interface{}) error {
|
||||||
|
if binding.Validator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return binding.Validator.ValidateStruct(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can do this!!!
|
||||||
|
// FormA is a external type that we can't modify it's tag
|
||||||
|
type FormA struct {
|
||||||
|
FieldA string `url:"field_a"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListHandler(s *Service) func(ctx *gin.Context) {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
var urlBinding = customerBinding{}
|
||||||
|
var opt FormA
|
||||||
|
err := ctx.MustBindWith(&opt, urlBinding)
|
||||||
|
if err != nil {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### http2 server push
|
### http2 server push
|
||||||
|
|
||||||
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
|
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
|
||||||
|
@ -20,15 +20,27 @@ type defaultValidator struct {
|
|||||||
|
|
||||||
type sliceValidateError []error
|
type sliceValidateError []error
|
||||||
|
|
||||||
|
// Error concatenates all error elements in sliceValidateError into a single string separated by \n.
|
||||||
func (err sliceValidateError) Error() string {
|
func (err sliceValidateError) Error() string {
|
||||||
var errMsgs []string
|
n := len(err)
|
||||||
for i, e := range err {
|
switch n {
|
||||||
if e == nil {
|
case 0:
|
||||||
continue
|
return ""
|
||||||
|
default:
|
||||||
|
var b strings.Builder
|
||||||
|
if err[0] != nil {
|
||||||
|
fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error())
|
||||||
}
|
}
|
||||||
errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error()))
|
if n > 1 {
|
||||||
|
for i := 1; i < n; i++ {
|
||||||
|
if err[i] != nil {
|
||||||
|
b.WriteString("\n")
|
||||||
|
fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
return strings.Join(errMsgs, "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ StructValidator = &defaultValidator{}
|
var _ StructValidator = &defaultValidator{}
|
||||||
|
20
binding/default_validator_benchmark_test.go
Normal file
20
binding/default_validator_benchmark_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSliceValidateError(b *testing.B) {
|
||||||
|
const size int = 100
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
e := make(sliceValidateError, size)
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
e[j] = errors.New(strconv.Itoa(j))
|
||||||
|
}
|
||||||
|
if len(e.Error()) == 0 {
|
||||||
|
b.Errorf("error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,26 @@ func TestSliceValidateError(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
|
{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
|
||||||
|
{"has zero elements", sliceValidateError{}, ""},
|
||||||
|
{"has one element", sliceValidateError{errors.New("test one error")}, "[0]: test one error"},
|
||||||
|
{"has two elements",
|
||||||
|
sliceValidateError{
|
||||||
|
errors.New("first error"),
|
||||||
|
errors.New("second error"),
|
||||||
|
},
|
||||||
|
"[0]: first error\n[1]: second error",
|
||||||
|
},
|
||||||
|
{"has many elements",
|
||||||
|
sliceValidateError{
|
||||||
|
errors.New("first error"),
|
||||||
|
errors.New("second error"),
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
errors.New("last error"),
|
||||||
|
},
|
||||||
|
"[0]: first error\n[1]: second error\n[5]: last error",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -34,6 +34,10 @@ func mapForm(ptr interface{}, form map[string][]string) error {
|
|||||||
return mapFormByTag(ptr, form, "form")
|
return mapFormByTag(ptr, form, "form")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error {
|
||||||
|
return mapFormByTag(ptr, form, tag)
|
||||||
|
}
|
||||||
|
|
||||||
var emptyField = reflect.StructField{}
|
var emptyField = reflect.StructField{}
|
||||||
|
|
||||||
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
||||||
|
@ -145,6 +145,15 @@ func TestMappingForm(t *testing.T) {
|
|||||||
assert.Equal(t, int(6), s.F)
|
assert.Equal(t, int(6), s.F)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapFormWithTag(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
F int `externalTag:"field"`
|
||||||
|
}
|
||||||
|
err := MapFormWithTag(&s, map[string][]string{"field": {"6"}}, "externalTag")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int(6), s.F)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMappingTime(t *testing.T) {
|
func TestMappingTime(t *testing.T) {
|
||||||
var s struct {
|
var s struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
Loading…
x
Reference in New Issue
Block a user