Merge remote-tracking branch 'upstream/master'

This commit is contained in:
thinkerou 2021-06-30 09:45:11 +08:00
commit 0132a404e0
6 changed files with 126 additions and 6 deletions

View File

@ -2021,6 +2021,61 @@ enough to call binding at once.
can be called by `c.ShouldBind()` multiple times without any damage to
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
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.

View File

@ -20,15 +20,27 @@ type defaultValidator struct {
type sliceValidateError []error
// Error concatenates all error elements in sliceValidateError into a single string separated by \n.
func (err sliceValidateError) Error() string {
var errMsgs []string
for i, e := range err {
if e == nil {
continue
n := len(err)
switch n {
case 0:
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{}

View 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")
}
}
}

View File

@ -16,6 +16,26 @@ func TestSliceValidateError(t *testing.T) {
want string
}{
{"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 {
t.Run(tt.name, func(t *testing.T) {

View File

@ -34,6 +34,10 @@ func mapForm(ptr interface{}, form map[string][]string) error {
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{}
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {

View File

@ -145,6 +145,15 @@ func TestMappingForm(t *testing.T) {
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) {
var s struct {
Time time.Time