[#2763] fixed multipart/form-data array value bind
This commit is contained in:
@@ -5,9 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// BindBody binds request body content to i.
|
||||
@@ -28,10 +30,12 @@ func BindBody(c echo.Context, i interface{}) error {
|
||||
return echo.NewHTTPErrorWithInternal(http.StatusBadRequest, err, err.Error())
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// fallback to the default binder
|
||||
return echo.BindBody(c, i)
|
||||
case strings.HasPrefix(ctype, echo.MIMEApplicationForm), strings.HasPrefix(ctype, echo.MIMEMultipartForm):
|
||||
return bindFormData(c, i)
|
||||
}
|
||||
|
||||
// fallback to the default binder
|
||||
return echo.BindBody(c, i)
|
||||
}
|
||||
|
||||
// CopyJsonBody reads the request body into i by
|
||||
@@ -57,3 +61,69 @@ func CopyJsonBody(r *http.Request, i interface{}) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// This is temp hotfix for properly binding multipart/form-data array values
|
||||
// when a map destination is used.
|
||||
//
|
||||
// It should be replaced with echo.BindBody(c, i) once the issue is fixed in echo.
|
||||
func bindFormData(c echo.Context, i interface{}) error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
values, err := c.FormValues()
|
||||
if err != nil {
|
||||
return echo.NewHTTPErrorWithInternal(http.StatusBadRequest, err, err.Error())
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
rt := reflect.TypeOf(i).Elem()
|
||||
|
||||
// map
|
||||
if rt.Kind() == reflect.Map {
|
||||
rv := reflect.ValueOf(i).Elem()
|
||||
|
||||
for k, v := range values {
|
||||
if total := len(v); total == 1 {
|
||||
rv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(normalizeMultipartValue(v[0])))
|
||||
} else {
|
||||
normalized := make([]any, total)
|
||||
for i, vItem := range v {
|
||||
normalized[i] = vItem
|
||||
}
|
||||
rv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(normalized))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// anything else
|
||||
return echo.BindBody(c, i)
|
||||
}
|
||||
|
||||
// In order to support more seamlessly both json and multipart/form-data requests,
|
||||
// the following normalization rules are applied for plain multipart string values:
|
||||
// - "true" is converted to the json `true`
|
||||
// - "false" is converted to the json `false`
|
||||
// - numeric (non-scientific) strings are converted to json number
|
||||
// - any other string (empty string too) is left as it is
|
||||
func normalizeMultipartValue(raw string) any {
|
||||
switch raw {
|
||||
case "true":
|
||||
return true
|
||||
case "false":
|
||||
return false
|
||||
default:
|
||||
if raw[0] >= '0' && raw[0] <= '9' {
|
||||
if v, err := cast.ToFloat64E(raw); err == nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user