diff --git a/rest/httpx/requests_test.go b/rest/httpx/requests_test.go index fd7fb3a5ac5e..791480a1c498 100644 --- a/rest/httpx/requests_test.go +++ b/rest/httpx/requests_test.go @@ -47,6 +47,21 @@ func TestParseForm(t *testing.T) { assert.Nil(t, Parse(r, &v)) assert.Equal(t, 0, len(v.NoValue)) }) + + t.Run("slice with one value on array format", func(t *testing.T) { + var v struct { + Names string `form:"names"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?names=1,2,3", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.Equal(t, "1,2,3", v.Names) + } + }) } func TestParseFormArray(t *testing.T) { @@ -114,7 +129,7 @@ func TestParseFormArray(t *testing.T) { http.NoBody) assert.NoError(t, err) if assert.NoError(t, Parse(r, &v)) { - assert.ElementsMatch(t, []string{""}, v.Name) + assert.Empty(t, v.Name) } }) @@ -129,7 +144,7 @@ func TestParseFormArray(t *testing.T) { http.NoBody) assert.NoError(t, err) if assert.NoError(t, Parse(r, &v)) { - assert.ElementsMatch(t, []string{"", "1"}, v.Name) + assert.ElementsMatch(t, []string{"1"}, v.Name) } }) @@ -192,6 +207,66 @@ func TestParseFormArray(t *testing.T) { assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names) } }) + + t.Run("slice with one empty value on integer array format", func(t *testing.T) { + var v struct { + Numbers []int `form:"numbers,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?numbers=", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.Empty(t, v.Numbers) + } + }) + + t.Run("slice with one value on integer array format", func(t *testing.T) { + var v struct { + Numbers []int `form:"numbers,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?numbers=&numbers=2", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []int{2}, v.Numbers) + } + }) + + t.Run("slice with one empty value on float64 array format", func(t *testing.T) { + var v struct { + Numbers []float64 `form:"numbers,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?numbers=", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.Empty(t, v.Numbers) + } + }) + + t.Run("slice with one value on float64 array format", func(t *testing.T) { + var v struct { + Numbers []float64 `form:"numbers,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?numbers=&numbers=2", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []float64{2}, v.Numbers) + } + }) } func TestParseForm_Error(t *testing.T) { diff --git a/rest/httpx/util.go b/rest/httpx/util.go index c22ad8e0d28b..ee8291296ed7 100644 --- a/rest/httpx/util.go +++ b/rest/httpx/util.go @@ -35,6 +35,16 @@ func GetFormValues(r *http.Request) (map[string]any, error) { for name, values := range r.Form { filtered := make([]string, 0, len(values)) for _, v := range values { + // ignore empty values, especially for optional int parameters + // e.g. /api?ids= + // e.g. /api + // type Req struct { + // IDs []int `form:"ids,optional"` + // } + if len(v) == 0 { + continue + } + if n < maxFormParamCount { filtered = append(filtered, v) n++