Skip to content

Commit b917b14

Browse files
guonaihongappleboy
andauthored
fix(binding): empty value error (#2169)
* fix empty value error Here is the code that can report an error ```go package main import ( "fmt" "github.com/gin-gonic/gin" "io" "net/http" "os" "time" ) type header struct { Duration time.Duration `header:"duration"` CreateTime time.Time `header:"createTime" time_format:"unix"` } func needFix1() { g := gin.Default() g.GET("/", func(c *gin.Context) { h := header{} err := c.ShouldBindHeader(&h) if err != nil { c.JSON(500, fmt.Sprintf("fail:%s\n", err)) return } c.JSON(200, h) }) g.Run(":8081") } func needFix2() { g := gin.Default() g.GET("/", func(c *gin.Context) { h := header{} err := c.ShouldBindHeader(&h) if err != nil { c.JSON(500, fmt.Sprintf("fail:%s\n", err)) return } c.JSON(200, h) }) g.Run(":8082") } func sendNeedFix1() { // send to needFix1 sendBadData("http://127.0.0.1:8081", "duration") } func sendNeedFix2() { // send to needFix2 sendBadData("http://127.0.0.1:8082", "createTime") } func sendBadData(url, key string) { req, err := http.NewRequest("GET", "http://127.0.0.1:8081", nil) if err != nil { fmt.Printf("err:%s\n", err) return } // Only the key and no value can cause an error req.Header.Add(key, "") rsp, err := http.DefaultClient.Do(req) if err != nil { return } io.Copy(os.Stdout, rsp.Body) rsp.Body.Close() } func main() { go needFix1() go needFix2() time.Sleep(time.Second / 1000 * 200) // 200ms sendNeedFix1() sendNeedFix2() } ``` * modify code * add comment * test(binding): use 'any' alias and require.NoError in form mapping tests - Replace 'interface{}' with 'any' alias in bindTestData struct - Change assert.NoError to require.NoError in TestMappingTimeUnixNano and TestMappingTimeDuration to fail fast on mapping errors --------- Co-authored-by: Bo-Yi Wu <[email protected]>
1 parent fad706f commit b917b14

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

binding/form_mapping.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
300300
}
301301

302302
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
303+
// If it is a string type, no spaces are removed, and the user data is not modified here
304+
if value.Kind() != reflect.String {
305+
val = strings.TrimSpace(val)
306+
}
307+
303308
switch value.Kind() {
304309
case reflect.Int:
305310
return setIntField(val, 0, value)
@@ -404,6 +409,11 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
404409
timeFormat = time.RFC3339
405410
}
406411

412+
if val == "" {
413+
value.Set(reflect.ValueOf(time.Time{}))
414+
return nil
415+
}
416+
407417
switch tf := strings.ToLower(timeFormat); tf {
408418
case "unix", "unixmilli", "unixmicro", "unixnano":
409419
tv, err := strconv.ParseInt(val, 10, 64)
@@ -427,11 +437,6 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
427437
return nil
428438
}
429439

430-
if val == "" {
431-
value.Set(reflect.ValueOf(time.Time{}))
432-
return nil
433-
}
434-
435440
l := time.Local
436441
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
437442
l = time.UTC
@@ -475,6 +480,10 @@ func setSlice(vals []string, value reflect.Value, field reflect.StructField) err
475480
}
476481

477482
func setTimeDuration(val string, value reflect.Value) error {
483+
if val == "" {
484+
val = "0"
485+
}
486+
478487
d, err := time.ParseDuration(val)
479488
if err != nil {
480489
return err

binding/form_mapping_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,35 @@ func TestMappingTime(t *testing.T) {
226226
require.Error(t, err)
227227
}
228228

229+
type bindTestData struct {
230+
need any
231+
got any
232+
in map[string][]string
233+
}
234+
235+
func TestMappingTimeUnixNano(t *testing.T) {
236+
type needFixUnixNanoEmpty struct {
237+
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
238+
}
239+
240+
// ok
241+
tests := []bindTestData{
242+
{need: &needFixUnixNanoEmpty{}, got: &needFixUnixNanoEmpty{}, in: formSource{"createTime": []string{" "}}},
243+
{need: &needFixUnixNanoEmpty{}, got: &needFixUnixNanoEmpty{}, in: formSource{"createTime": []string{}}},
244+
}
245+
246+
for _, v := range tests {
247+
err := mapForm(v.got, v.in)
248+
require.NoError(t, err)
249+
assert.Equal(t, v.need, v.got)
250+
}
251+
}
252+
229253
func TestMappingTimeDuration(t *testing.T) {
254+
type needFixDurationEmpty struct {
255+
Duration time.Duration `form:"duration"`
256+
}
257+
230258
var s struct {
231259
D time.Duration
232260
}
@@ -236,6 +264,17 @@ func TestMappingTimeDuration(t *testing.T) {
236264
require.NoError(t, err)
237265
assert.Equal(t, 5*time.Second, s.D)
238266

267+
// ok
268+
tests := []bindTestData{
269+
{need: &needFixDurationEmpty{}, got: &needFixDurationEmpty{}, in: formSource{"duration": []string{" "}}},
270+
{need: &needFixDurationEmpty{}, got: &needFixDurationEmpty{}, in: formSource{"duration": []string{}}},
271+
}
272+
273+
for _, v := range tests {
274+
err := mapForm(v.got, v.in)
275+
require.NoError(t, err)
276+
assert.Equal(t, v.need, v.got)
277+
}
239278
// error
240279
err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
241280
require.Error(t, err)

0 commit comments

Comments
 (0)