-
Notifications
You must be signed in to change notification settings - Fork 8.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array collection format in form binding #2750
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,13 @@ type FooStructForSliceType struct { | |
SliceFoo []int `form:"slice_foo"` | ||
} | ||
|
||
type FooStructForCollectionFormatTag struct { | ||
SliceMulti []int `form:"slice_multi" collection_format:"multi"` | ||
SliceCsv []int `form:"slice_csv" collection_format:"csv"` | ||
SliceSsv []int `form:"slice_ssv" collection_format:"ssv"` | ||
SlicePipes []int `form:"slice_pipes" collection_format:"pipes"` | ||
} | ||
|
||
type FooStructForStructType struct { | ||
StructFoo struct { | ||
Idx int `form:"idx"` | ||
|
@@ -311,6 +318,15 @@ func TestBindingFormInvalidName2(t *testing.T) { | |
"map_foo=bar", "bar2=foo") | ||
} | ||
|
||
func TestBindingFormCollectionFormat(t *testing.T) { | ||
testFormBindingForCollectionFormat(t, "POST", | ||
"/?slice_multi=1&slice_multi=2&slice_csv=1,2&slice_ssv=1 2&slice_pipes=1|2", "/", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing fuzzy tests here. Should try it with string type and see what happens if we feed pipes to csv param, csv to pipe param, csv to multi param etc… |
||
"", "") | ||
testFormBindingForCollectionFormat(t, "POST", | ||
"/", "/", | ||
"slice_multi=1&slice_multi=2&slice_csv=1,2&slice_ssv=1 2&slice_pipes=1|2", "") | ||
} | ||
|
||
func TestBindingFormForType(t *testing.T) { | ||
testFormBindingForType(t, "POST", | ||
"/", "/", | ||
|
@@ -1064,6 +1080,24 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB | |
assert.Error(t, err) | ||
} | ||
|
||
func testFormBindingForCollectionFormat(t *testing.T, method, path, badPath, body, badBody string) { | ||
b := Form | ||
assert.Equal(t, "form", b.Name()) | ||
|
||
obj := FooStructForCollectionFormatTag{} | ||
req := requestWithBody(method, path, body) | ||
if method == "POST" { | ||
req.Header.Add("Content-Type", MIMEPOSTForm) | ||
} | ||
err := b.Bind(req, &obj) | ||
assert.NoError(t, err) | ||
|
||
assert.Equal(t, []int{1, 2}, obj.SliceMulti) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd refrain from using values 1 and 2 for each one of them. It can potentially conceal misbehaviour. |
||
assert.Equal(t, []int{1, 2}, obj.SliceCsv) | ||
assert.Equal(t, []int{1, 2}, obj.SliceCsv) | ||
assert.Equal(t, []int{1, 2}, obj.SlicePipes) | ||
} | ||
|
||
func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) { | ||
b := Form | ||
assert.Equal(t, "form", b.Name()) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -174,11 +174,15 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][ | |
case reflect.Slice: | ||
if !ok { | ||
vs = []string{opt.defaultValue} | ||
} else { | ||
vs = split(vs, field) | ||
} | ||
return true, setSlice(vs, value, field) | ||
case reflect.Array: | ||
if !ok { | ||
vs = []string{opt.defaultValue} | ||
} else { | ||
vs = split(vs, field) | ||
} | ||
if len(vs) != value.Len() { | ||
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) | ||
|
@@ -376,6 +380,25 @@ func head(str, sep string) (head string, tail string) { | |
return str[:idx], str[idx+len(sep):] | ||
} | ||
|
||
func split(vals []string, field reflect.StructField) []string { | ||
if cfTag := field.Tag.Get("collection_format"); cfTag != "" { | ||
sep := "multi" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will stay as |
||
switch cfTag { | ||
case "csv": | ||
sep = "," | ||
case "ssv": | ||
sep = " " | ||
case "pipes": | ||
sep = "|" | ||
} | ||
|
||
if sep != "multi" && len(vals) == 1 { | ||
return strings.Split(vals[0], sep) | ||
} | ||
} | ||
return vals | ||
} | ||
|
||
func setFormMap(ptr interface{}, form map[string][]string) error { | ||
el := reflect.TypeOf(ptr).Elem() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example in
README.md
shows curl withGET
but test only testsPOST
.