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 | ||||
|  | ||||
| import "net/http" | ||||
| import ( | ||||
| 	"mime/multipart" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| ) | ||||
|  | ||||
| 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 { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := mapForm(obj, req.MultipartForm.Value); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := mapFiles(obj, req); err != nil { | ||||
| 	if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	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 ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| @ -16,34 +15,6 @@ import ( | ||||
| 	"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") | ||||
|  | ||||
| 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{} | ||||
|  | ||||
| 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 | ||||
| } | ||||
|  | ||||
| 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() | ||||
|  | ||||
| 	if vKind == reflect.Ptr { | ||||
| @ -71,7 +60,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s | ||||
| 			isNew = true | ||||
| 			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 { | ||||
| 			return false, err | ||||
| 		} | ||||
| @ -81,7 +70,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s | ||||
| 		return isSetted, nil | ||||
| 	} | ||||
|  | ||||
| 	ok, err := tryToSetValue(value, field, form, tag) | ||||
| 	ok, err := tryToSetValue(value, field, setter, tag) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| @ -97,7 +86,7 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s | ||||
| 			if !value.Field(i).CanSet() { | ||||
| 				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 { | ||||
| 				return false, err | ||||
| 			} | ||||
| @ -108,9 +97,14 @@ func mapping(value reflect.Value, field reflect.StructField, form map[string][]s | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
| func tryToSetValue(value reflect.Value, field reflect.StructField, form map[string][]string, tag string) (bool, error) { | ||||
| 	var tagValue, defaultValue string | ||||
| 	var isDefaultExists bool | ||||
| type setOptions struct { | ||||
| 	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, opts := head(tagValue, ",") | ||||
| @ -132,25 +126,29 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, form map[stri | ||||
| 		k, v := head(opt, "=") | ||||
| 		switch k { | ||||
| 		case "default": | ||||
| 			isDefaultExists = true | ||||
| 			defaultValue = v | ||||
| 			setOpt.isDefaultExists = true | ||||
| 			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] | ||||
| 	if !ok && !isDefaultExists { | ||||
| 	if !ok && !opt.isDefaultExists { | ||||
| 		return false, nil | ||||
| 	} | ||||
|  | ||||
| 	switch value.Kind() { | ||||
| 	case reflect.Slice: | ||||
| 		if !ok { | ||||
| 			vs = []string{defaultValue} | ||||
| 			vs = []string{opt.defaultValue} | ||||
| 		} | ||||
| 		return true, setSlice(vs, value, field) | ||||
| 	case reflect.Array: | ||||
| 		if !ok { | ||||
| 			vs = []string{defaultValue} | ||||
| 			vs = []string{opt.defaultValue} | ||||
| 		} | ||||
| 		if len(vs) != value.Len() { | ||||
| 			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: | ||||
| 		var val string | ||||
| 		if !ok { | ||||
| 			val = defaultValue | ||||
| 			val = opt.defaultValue | ||||
| 		} | ||||
|  | ||||
| 		if len(vs) > 0 { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user