From b5d33f067839b8203883fc081dbdea976ae3c913 Mon Sep 17 00:00:00 2001 From: wuyangfan Date: Mon, 25 May 2026 10:41:01 +0800 Subject: [PATCH] fix(bind): restore []string values for map[string]interface{} duplicates When binding multipart/form-data to map[string]interface{}, store a single value as string and multiple values as []string. This matches the behavior before v4.13.0 and the intent of #2656. Fixes #2731 Co-authored-by: Cursor --- bind.go | 8 +++++--- bind_test.go | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/bind.go b/bind.go index 1d4fe6f0a..1e8393735 100644 --- a/bind.go +++ b/bind.go @@ -181,9 +181,11 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri if isElemString { val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) } else if isElemInterface { - // To maintain backward compatibility, we always bind to the first string value - // and not the slice of strings when dealing with map[string]interface{}{} - val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) + if len(v) == 1 { + val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) + } else { + val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v)) + } } else { val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v)) } diff --git a/bind_test.go b/bind_test.go index 3e387ba19..9c6e1048a 100644 --- a/bind_test.go +++ b/bind_test.go @@ -498,7 +498,7 @@ func TestDefaultBinder_bindDataToMap(t *testing.T) { assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param", nil)) assert.Equal(t, map[string]interface{}{ - "multiple": "1", + "multiple": []string{"1", "2"}, "single": "3", }, dest, @@ -510,7 +510,7 @@ func TestDefaultBinder_bindDataToMap(t *testing.T) { assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param", nil)) assert.Equal(t, map[string]interface{}{ - "multiple": "1", + "multiple": []string{"1", "2"}, "single": "3", }, dest, @@ -542,6 +542,24 @@ func TestDefaultBinder_bindDataToMap(t *testing.T) { }) } +func TestBindMultipartFormToMapInterface(t *testing.T) { + bodyBuffer := new(bytes.Buffer) + mw := multipart.NewWriter(bodyBuffer) + assert.NoError(t, mw.WriteField("ima_slice", "WEBHOOK")) + assert.NoError(t, mw.WriteField("ima_slice", "OTHER")) + assert.NoError(t, mw.Close()) + + e := New() + req := httptest.NewRequest(http.MethodPost, "/", bodyBuffer) + req.Header.Set(HeaderContentType, mw.FormDataContentType()) + c := e.NewContext(req, nil) + + data := map[string]interface{}{} + err := c.Bind(&data) + assert.NoError(t, err) + assert.Equal(t, []string{"WEBHOOK", "OTHER"}, data["ima_slice"]) +} + func TestBindbindData(t *testing.T) { ts := new(bindTestStruct) b := new(DefaultBinder)