Skip to content

Commit

Permalink
Adding a new config to DefaultBinder
Browse files Browse the repository at this point in the history
Now the DefaultBinder could be configured to avoid binding struct fields
by name. This is particularly useful when the user don't want to bind
certain struct fields (with this config in true, only the tagged fields
will be binded)

Fixes labstack#1620, fixes labstack#1631, partially fixes labstack#1670
  • Loading branch information
pafuent committed Nov 10, 2020
1 parent ceffc10 commit e63fa85
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 2 deletions.
9 changes: 7 additions & 2 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ type (
}

// DefaultBinder is the default implementation of the Binder interface.
DefaultBinder struct{}
DefaultBinder struct {
// AvoidBindByFieldName avoid binding struct fields by name automatically. If it's set to true, the binding is
// only performed when a valid tag is pressent
AvoidBindByFieldName bool
}

// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
// Types that don't implement this, but do implement encoding.TextUnmarshaler
Expand Down Expand Up @@ -113,13 +117,14 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
inputFieldName := typeField.Tag.Get(tag)

if inputFieldName == "" {
inputFieldName = typeField.Name
// If tag is nil, we inspect if the field is a struct.
if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct {
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
return err
}
continue
} else if b.AvoidBindByFieldName == false {
inputFieldName = typeField.Name
}
}

Expand Down
35 changes: 35 additions & 0 deletions bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,23 @@ func TestBindbindData(t *testing.T) {
assertBindTestStruct(assert, ts)
}

func TestBindbindDataByTags(t *testing.T) {
assert := assert.New(t)
ts := new(bindTestStructWithTags)
b := new(DefaultBinder)
b.bindData(ts, values, "form")
assertBindTestStruct(assert, (*bindTestStruct)(ts))
}

func TestBindbindDataAvoidBindByFieldName(t *testing.T) {
assert := assert.New(t)
ts := new(bindTestStruct)
b := new(DefaultBinder)
b.AvoidBindByFieldName = true
b.bindData(ts, values, "form")
assertBindTestStructDefaultValues(assert, ts)
}

func TestBindParam(t *testing.T) {
e := New()
req := httptest.NewRequest(GET, "/", nil)
Expand Down Expand Up @@ -516,6 +533,24 @@ func assertBindTestStruct(a *assert.Assertions, ts *bindTestStruct) {
a.Equal("", ts.GetCantSet())
}

func assertBindTestStructDefaultValues(a *assert.Assertions, ts *bindTestStruct) {
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.GetCantSet())
}

func testBindOkay(assert *assert.Assertions, r io.Reader, ctype string) {
e := New()
req := httptest.NewRequest(http.MethodPost, "/", r)
Expand Down

0 comments on commit e63fa85

Please sign in to comment.