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:
		| @ -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) | ||||||
|  | } | ||||||
|  | |||||||
| @ -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 { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user