refactor(form_mapping.go): mapping multipart request (#1829)

* refactor(form_mapping.go): mapping multipart request

* add checkers for a types to match with the setter interface

* form_mapping.go: rename method name on setter interface, add comments

* fix style of comments
This commit is contained in:
Dmitry Kutakov 2019-04-02 04:01:34 +03:00 committed by 田欧
parent ce20f107f5
commit 2e915f4e50
2 changed files with 71 additions and 49 deletions

View File

@ -4,7 +4,11 @@
package binding package binding
import "net/http" import (
"mime/multipart"
"net/http"
"reflect"
)
const defaultMemory = 32 * 1024 * 1024 const defaultMemory = 32 * 1024 * 1024
@ -53,13 +57,33 @@ func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseMultipartForm(defaultMemory); err != nil { if err := req.ParseMultipartForm(defaultMemory); err != nil {
return err return err
} }
if err := mapForm(obj, req.MultipartForm.Value); err != nil { if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil {
return err
}
if err := mapFiles(obj, req); err != nil {
return err return err
} }
return validate(obj) return validate(obj)
} }
type multipartRequest http.Request
var _ setter = (*multipartRequest)(nil)
var (
multipartFileHeaderStructType = reflect.TypeOf(multipart.FileHeader{})
)
// TrySet tries to set a value by the multipart request with the binding a form file
func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
if value.Type() == multipartFileHeaderStructType {
_, file, err := (*http.Request)(r).FormFile(key)
if err != nil {
return false, err
}
if file != nil {
value.Set(reflect.ValueOf(*file))
return true, nil
}
}
return setByForm(value, field, r.MultipartForm.Value, key, opt)
}

View File

@ -7,7 +7,6 @@ package binding
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/http"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -16,34 +15,6 @@ import (
"github.com/gin-gonic/gin/internal/json" "github.com/gin-gonic/gin/internal/json"
) )
func mapFiles(ptr interface{}, req *http.Request) error {
typ := reflect.TypeOf(ptr).Elem()
val := reflect.ValueOf(ptr).Elem()
for i := 0; i < typ.NumField(); i++ {
typeField := typ.Field(i)
structField := val.Field(i)
t := fmt.Sprintf("%s", typeField.Type)
if string(t) != "*multipart.FileHeader" {
continue
}
inputFieldName := typeField.Tag.Get("form")
if inputFieldName == "" {
inputFieldName = typeField.Name
}
_, fileHeader, err := req.FormFile(inputFieldName)
if err != nil {
return err
}
structField.Set(reflect.ValueOf(fileHeader))
}
return nil
}
var errUnknownType = errors.New("Unknown type") var errUnknownType = errors.New("Unknown type")
func mapUri(ptr interface{}, m map[string][]string) error { func mapUri(ptr interface{}, m map[string][]string) error {
@ -57,11 +28,29 @@ func mapForm(ptr interface{}, form map[string][]string) error {
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 {
_, err := mapping(reflect.ValueOf(ptr), emptyField, form, tag) return mappingByPtr(ptr, formSource(form), tag)
}
// setter tries to set value on a walking by fields of a struct
type setter interface {
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
}
type formSource map[string][]string
var _ setter = formSource(nil)
// TrySet tries to set a value by request's form source (like map[string][]string)
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
return setByForm(value, field, form, tagValue, opt)
}
func mappingByPtr(ptr interface{}, setter setter, tag string) error {
_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
return err return err
} }
func mapping(value reflect.Value, field reflect.StructField, form map[string][]string, tag string) (bool, error) { func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
var vKind = value.Kind() var vKind = value.Kind()
if vKind == reflect.Ptr { if vKind == reflect.Ptr {
@ -71,7 +60,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s
isNew = true isNew = true
vPtr = reflect.New(value.Type().Elem()) vPtr = reflect.New(value.Type().Elem())
} }
isSetted, err := mapping(vPtr.Elem(), field, form, tag) isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -81,7 +70,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s
return isSetted, nil return isSetted, nil
} }
ok, err := tryToSetValue(value, field, form, tag) ok, err := tryToSetValue(value, field, setter, tag)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -97,7 +86,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s
if !value.Field(i).CanSet() { if !value.Field(i).CanSet() {
continue continue
} }
ok, err := mapping(value.Field(i), tValue.Field(i), form, tag) ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -108,9 +97,14 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s
return false, nil return false, nil
} }
func tryToSetValue(value reflect.Value, field reflect.StructField, form map[string][]string, tag string) (bool, error) { type setOptions struct {
var tagValue, defaultValue string isDefaultExists bool
var isDefaultExists bool defaultValue string
}
func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
var tagValue string
var setOpt setOptions
tagValue = field.Tag.Get(tag) tagValue = field.Tag.Get(tag)
tagValue, opts := head(tagValue, ",") tagValue, opts := head(tagValue, ",")
@ -132,25 +126,29 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, form map[stri
k, v := head(opt, "=") k, v := head(opt, "=")
switch k { switch k {
case "default": case "default":
isDefaultExists = true setOpt.isDefaultExists = true
defaultValue = v setOpt.defaultValue = v
} }
} }
return setter.TrySet(value, field, tagValue, setOpt)
}
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
vs, ok := form[tagValue] vs, ok := form[tagValue]
if !ok && !isDefaultExists { if !ok && !opt.isDefaultExists {
return false, nil return false, nil
} }
switch value.Kind() { switch value.Kind() {
case reflect.Slice: case reflect.Slice:
if !ok { if !ok {
vs = []string{defaultValue} vs = []string{opt.defaultValue}
} }
return true, setSlice(vs, value, field) return true, setSlice(vs, value, field)
case reflect.Array: case reflect.Array:
if !ok { if !ok {
vs = []string{defaultValue} vs = []string{opt.defaultValue}
} }
if len(vs) != value.Len() { if len(vs) != value.Len() {
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
@ -159,7 +157,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, form map[stri
default: default:
var val string var val string
if !ok { if !ok {
val = defaultValue val = opt.defaultValue
} }
if len(vs) > 0 { if len(vs) > 0 {