From cc63183b058f6d20f36abfe835b8594c4d1ac448 Mon Sep 17 00:00:00 2001 From: lowgame <128625014+lowgame@users.noreply.github.com> Date: Sun, 18 Oct 2026 22:01:20 +0300 Subject: [PATCH] fix: bind valid slices after earlier errors --- binder.go | 36 +++++++++++++++++++++--------------- binder_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/binder.go b/binder.go index 7ae3709fa..f5f7a6088 100644 --- a/binder.go +++ b/binder.go @@ -575,6 +575,7 @@ func (b *ValueBinder) intsValue(sourceParam string, dest any, valueMustExist boo } func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *ValueBinder { + errorsBefore := len(b.errors) switch d := dest.(type) { case *[]int64: tmp := make([]int64, len(values)) @@ -584,7 +585,7 @@ func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *Value return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]int32: @@ -595,7 +596,7 @@ func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *Value return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]int16: @@ -606,7 +607,7 @@ func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *Value return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]int8: @@ -617,7 +618,7 @@ func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *Value return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]int: @@ -628,7 +629,7 @@ func (b *ValueBinder) ints(sourceParam string, values []string, dest any) *Value return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } } @@ -803,6 +804,7 @@ func (b *ValueBinder) uintsValue(sourceParam string, dest any, valueMustExist bo } func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *ValueBinder { + errorsBefore := len(b.errors) switch d := dest.(type) { case *[]uint64: tmp := make([]uint64, len(values)) @@ -812,7 +814,7 @@ func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *Valu return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]uint32: @@ -823,7 +825,7 @@ func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *Valu return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]uint16: @@ -834,7 +836,7 @@ func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *Valu return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]uint8: // byte is alias to uint8 @@ -845,7 +847,7 @@ func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *Valu return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]uint: @@ -856,7 +858,7 @@ func (b *ValueBinder) uints(sourceParam string, values []string, dest any) *Valu return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } } @@ -965,6 +967,7 @@ func (b *ValueBinder) boolsValue(sourceParam string, dest *[]bool, valueMustExis } func (b *ValueBinder) bools(sourceParam string, values []string, dest *[]bool) *ValueBinder { + errorsBefore := len(b.errors) tmp := make([]bool, len(values)) for i, v := range values { b.bool(sourceParam, v, &tmp[i]) @@ -972,7 +975,7 @@ func (b *ValueBinder) bools(sourceParam string, values []string, dest *[]bool) * return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *dest = tmp } return b @@ -1056,6 +1059,7 @@ func (b *ValueBinder) floatsValue(sourceParam string, dest any, valueMustExist b } func (b *ValueBinder) floats(sourceParam string, values []string, dest any) *ValueBinder { + errorsBefore := len(b.errors) switch d := dest.(type) { case *[]float64: tmp := make([]float64, len(values)) @@ -1065,7 +1069,7 @@ func (b *ValueBinder) floats(sourceParam string, values []string, dest any) *Val return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } case *[]float32: @@ -1076,7 +1080,7 @@ func (b *ValueBinder) floats(sourceParam string, values []string, dest any) *Val return b } } - if b.errors == nil { + if len(b.errors) == errorsBefore { *d = tmp } } @@ -1157,6 +1161,7 @@ func (b *ValueBinder) times(sourceParam string, dest *[]time.Time, layout string return b } + errorsBefore := len(b.errors) tmp := make([]time.Time, len(values)) for i, v := range values { t, err := time.Parse(layout, v) @@ -1169,7 +1174,7 @@ func (b *ValueBinder) times(sourceParam string, dest *[]time.Time, layout string } tmp[i] = t } - if b.errors == nil { + if len(b.errors) == errorsBefore { *dest = tmp } return b @@ -1232,6 +1237,7 @@ func (b *ValueBinder) durationsValue(sourceParam string, dest *[]time.Duration, } func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]time.Duration) *ValueBinder { + errorsBefore := len(b.errors) tmp := make([]time.Duration, len(values)) for i, v := range values { t, err := time.ParseDuration(v) @@ -1244,7 +1250,7 @@ func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]tim } tmp[i] = t } - if b.errors == nil { + if len(b.errors) == errorsBefore { *dest = tmp } return b diff --git a/binder_test.go b/binder_test.go index 8eced8208..01f047baf 100644 --- a/binder_test.go +++ b/binder_test.go @@ -671,6 +671,38 @@ func TestValueBinder_Int_errorMessage(t *testing.T) { assert.EqualError(t, errs[1], `code=400, message=failed to bind field value to uint, err=strconv.ParseUint: parsing "nope": invalid syntax, field=param`) } +func TestValueBinder_FailFastFalseBindsValidSlicesAfterError(t *testing.T) { + c := createTestContext("/?bad=nope&ints=1&ints=2&uints=3&uints=4&bools=true&bools=false&floats=1.5&floats=2.5×=2024-01-02×=2024-01-03&durations=1s&durations=2s", nil, nil) + + var bad int + ints := []int{99} + uints := []uint{99} + bools := []bool{false} + floats := []float64{99} + times := []time.Time{{}} + durations := []time.Duration{99} + errs := QueryParamsBinder(c).FailFast(false). + Int("bad", &bad). + Ints("ints", &ints). + Uints("uints", &uints). + Bools("bools", &bools). + Float64s("floats", &floats). + Times("times", ×, time.DateOnly). + Durations("durations", &durations). + BindErrors() + + assert.Len(t, errs, 1) + assert.Equal(t, []int{1, 2}, ints) + assert.Equal(t, []uint{3, 4}, uints) + assert.Equal(t, []bool{true, false}, bools) + assert.Equal(t, []float64{1.5, 2.5}, floats) + assert.Equal(t, []time.Time{ + time.Date(2024, time.January, 2, 0, 0, 0, 0, time.UTC), + time.Date(2024, time.January, 3, 0, 0, 0, 0, time.UTC), + }, times) + assert.Equal(t, []time.Duration{time.Second, 2 * time.Second}, durations) +} + func TestValueBinder_Uint64_uintValue(t *testing.T) { var testCases = []struct { name string