1
0
mirror of https://github.com/labstack/echo.git synced 2025-07-15 01:34:53 +02:00

Fix #1729 Binding query/path params and form fields to struct only works for explicit tags (#1734)

* Binding query/path params and form fields to struct only works for fields that have explicit TAG defined on struct
* remove unnecessary benchmark after change because it is not valid test anymore
This commit is contained in:
Martti T
2021-01-05 12:04:24 +02:00
committed by GitHub
parent f718079658
commit 02ed3f3126
2 changed files with 69 additions and 35 deletions

View File

@ -160,6 +160,31 @@ var values = map[string][]string{
"ST": {"bar"},
}
func TestToMultipleFields(t *testing.T) {
e := New()
req := httptest.NewRequest(http.MethodGet, "/?id=1&ID=2", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
type Root struct {
ID int64 `query:"id"`
Child2 struct {
ID int64
}
Child1 struct {
ID int64 `query:"id"`
}
}
u := new(Root)
err := c.Bind(u)
if assert.NoError(t, err) {
assert.Equal(t, int64(1), u.ID) // perfectly reasonable
assert.Equal(t, int64(1), u.Child1.ID) // untagged struct containing tagged field gets filled (by tag)
assert.Equal(t, int64(0), u.Child2.ID) // untagged struct containing untagged field should not be bind
}
}
func TestBindJSON(t *testing.T) {
assert := assert.New(t)
testBindOkay(assert, strings.NewReader(userJSON), MIMEApplicationJSON)
@ -238,10 +263,13 @@ func TestBindUnmarshalParam(t *testing.T) {
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
result := struct {
T Timestamp `query:"ts"`
TA []Timestamp `query:"ta"`
SA StringArray `query:"sa"`
ST Struct
T Timestamp `query:"ts"`
TA []Timestamp `query:"ta"`
SA StringArray `query:"sa"`
ST Struct
StWithTag struct {
Foo string `query:"st"`
}
}{}
err := c.Bind(&result)
ts := Timestamp(time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC))
@ -252,7 +280,8 @@ func TestBindUnmarshalParam(t *testing.T) {
assert.Equal(ts, result.T)
assert.Equal(StringArray([]string{"one", "two", "three"}), result.SA)
assert.Equal([]Timestamp{ts, ts}, result.TA)
assert.Equal(Struct{"baz"}, result.ST)
assert.Equal(Struct{""}, result.ST) // child struct does not have a field with matching tag
assert.Equal("baz", result.StWithTag.Foo) // child struct has field with matching tag
}
}
@ -274,7 +303,7 @@ func TestBindUnmarshalText(t *testing.T) {
assert.Equal(t, ts, result.T)
assert.Equal(t, StringArray([]string{"one", "two", "three"}), result.SA)
assert.Equal(t, []time.Time{ts, ts}, result.TA)
assert.Equal(t, Struct{"baz"}, result.ST)
assert.Equal(t, Struct{""}, result.ST) // field in child struct does not have tag
}
}
@ -323,11 +352,27 @@ func TestBindUnsupportedMediaType(t *testing.T) {
}
func TestBindbindData(t *testing.T) {
assert := assert.New(t)
a := assert.New(t)
ts := new(bindTestStruct)
b := new(DefaultBinder)
b.bindData(ts, values, "form")
assertBindTestStruct(assert, ts)
err := b.bindData(ts, values, "form")
a.NoError(err)
a.Equal(0, ts.I)
a.Equal(int8(0), ts.I8)
a.Equal(int16(0), ts.I16)
a.Equal(int32(0), ts.I32)
a.Equal(int64(0), ts.I64)
a.Equal(uint(0), ts.UI)
a.Equal(uint8(0), ts.UI8)
a.Equal(uint16(0), ts.UI16)
a.Equal(uint32(0), ts.UI32)
a.Equal(uint64(0), ts.UI64)
a.Equal(false, ts.B)
a.Equal(float32(0), ts.F32)
a.Equal(float64(0), ts.F64)
a.Equal("", ts.S)
a.Equal("", ts.cantSet)
}
func TestBindParam(t *testing.T) {
@ -470,20 +515,6 @@ func TestBindSetFields(t *testing.T) {
}
}
func BenchmarkBindbindData(b *testing.B) {
b.ReportAllocs()
assert := assert.New(b)
ts := new(bindTestStruct)
binder := new(DefaultBinder)
var err error
b.ResetTimer()
for i := 0; i < b.N; i++ {
err = binder.bindData(ts, values, "form")
}
assert.NoError(err)
assertBindTestStruct(assert, ts)
}
func BenchmarkBindbindDataWithTags(b *testing.B) {
b.ReportAllocs()
assert := assert.New(b)
@ -560,8 +591,9 @@ func TestDefaultBinder_BindToStructFromMixedSources(t *testing.T) {
// these tests are to document this behaviour and detect further possible regressions when bind implementation is changed
type Opts struct {
ID int `json:"id"`
Node string `json:"node"`
ID int `json:"id" form:"id" query:"id"`
Node string `json:"node" form:"node" query:"node" param:"node"`
Lang string
}
var testCases = []struct {
@ -727,8 +759,8 @@ func TestDefaultBinder_BindBody(t *testing.T) {
// these tests are to document this behaviour and detect further possible regressions when bind implementation is changed
type Node struct {
ID int `json:"id" xml:"id"`
Node string `json:"node" xml:"node"`
ID int `json:"id" xml:"id" form:"id" query:"id"`
Node string `json:"node" xml:"node" form:"node" query:"node" param:"node"`
}
type Nodes struct {
Nodes []Node `xml:"node" form:"node"`
@ -824,7 +856,7 @@ func TestDefaultBinder_BindBody(t *testing.T) {
expectError: "code=400, message=Syntax error: line=1, error=XML syntax error on line 1: unexpected EOF, internal=XML syntax error on line 1: unexpected EOF",
},
{
name: "ok, FORM POST bind to struct with: path + query + empty body",
name: "ok, FORM POST bind to struct with: path + query + body",
givenURL: "/api/real_node/endpoint?node=xxx",
givenMethod: http.MethodPost,
givenContentType: MIMEApplicationForm,