feat: support query array in httpx.Parse (#4440)

This commit is contained in:
Kevin Wan
2024-11-02 21:55:37 +08:00
committed by GitHub
parent f822c9a94f
commit 18cb3141ba
6 changed files with 157 additions and 9 deletions

View File

@@ -23,9 +23,16 @@ const (
)
var (
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
validator atomic.Value
formUnmarshaler = mapping.NewUnmarshaler(
formKey,
mapping.WithStringValues(),
mapping.WithOpaqueKeys(),
mapping.WithFromArray())
pathUnmarshaler = mapping.NewUnmarshaler(
pathKey,
mapping.WithStringValues(),
mapping.WithOpaqueKeys())
validator atomic.Value
)
// Validator defines the interface for validating the request.

View File

@@ -49,6 +49,61 @@ func TestParseForm(t *testing.T) {
})
}
func TestParseFormArray(t *testing.T) {
t.Run("slice", func(t *testing.T) {
var v struct {
Name []string `form:"name"`
Age []int `form:"age"`
Percent []float64 `form:"percent,optional"`
}
r, err := http.NewRequest(
http.MethodGet,
"/a?name=hello&name=world&age=18&age=19&percent=3.4&percent=4.5",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"hello", "world"}, v.Name)
assert.ElementsMatch(t, []int{18, 19}, v.Age)
assert.ElementsMatch(t, []float64{3.4, 4.5}, v.Percent)
}
})
t.Run("slice with single value", func(t *testing.T) {
var v struct {
Name []string `form:"name"`
Age []int `form:"age"`
Percent []float64 `form:"percent,optional"`
}
r, err := http.NewRequest(
http.MethodGet,
"/a?name=hello&age=18&percent=3.4",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"hello"}, v.Name)
assert.ElementsMatch(t, []int{18}, v.Age)
assert.ElementsMatch(t, []float64{3.4}, v.Percent)
}
})
t.Run("slice with empty and non-empty", func(t *testing.T) {
var v struct {
Name []string `form:"name"`
}
r, err := http.NewRequest(
http.MethodGet,
"/a?name=&name=1",
http.NoBody)
assert.NoError(t, err)
if assert.NoError(t, Parse(r, &v)) {
assert.ElementsMatch(t, []string{"1"}, v.Name)
}
})
}
func TestParseForm_Error(t *testing.T) {
var v struct {
Name string `form:"name"`

View File

@@ -20,10 +20,16 @@ func GetFormValues(r *http.Request) (map[string]any, error) {
}
params := make(map[string]any, len(r.Form))
for name := range r.Form {
formValue := r.Form.Get(name)
if len(formValue) > 0 {
params[name] = formValue
for name, values := range r.Form {
filtered := make([]string, 0, len(values))
for _, v := range values {
if len(v) > 0 {
filtered = append(filtered, v)
}
}
if len(filtered) > 0 {
params[name] = filtered
}
}

View File

@@ -137,7 +137,7 @@ func TestPatRouter(t *testing.T) {
}
func TestParseSlice(t *testing.T) {
body := `names=%5B%22first%22%2C%22second%22%5D`
body := `names=first&names=second`
reader := strings.NewReader(body)
r, err := http.NewRequest(http.MethodPost, "http://hello.com/", reader)
assert.Nil(t, err)
@@ -388,7 +388,7 @@ func TestParseQueryRequired(t *testing.T) {
}
func TestParseOptional(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil)
r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever", nil)
assert.Nil(t, err)
router := NewRouter()