mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-04 22:35:38 +08:00
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 <appleboy.tw@gmail.com>
This commit is contained in:
parent
fad706f121
commit
b917b14ff9
@ -300,6 +300,11 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
|
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
|
||||||
|
// If it is a string type, no spaces are removed, and the user data is not modified here
|
||||||
|
if value.Kind() != reflect.String {
|
||||||
|
val = strings.TrimSpace(val)
|
||||||
|
}
|
||||||
|
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
return setIntField(val, 0, value)
|
return setIntField(val, 0, value)
|
||||||
@ -404,6 +409,11 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
|
|||||||
timeFormat = time.RFC3339
|
timeFormat = time.RFC3339
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val == "" {
|
||||||
|
value.Set(reflect.ValueOf(time.Time{}))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
switch tf := strings.ToLower(timeFormat); tf {
|
switch tf := strings.ToLower(timeFormat); tf {
|
||||||
case "unix", "unixmilli", "unixmicro", "unixnano":
|
case "unix", "unixmilli", "unixmicro", "unixnano":
|
||||||
tv, err := strconv.ParseInt(val, 10, 64)
|
tv, err := strconv.ParseInt(val, 10, 64)
|
||||||
@ -427,11 +437,6 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val == "" {
|
|
||||||
value.Set(reflect.ValueOf(time.Time{}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
l := time.Local
|
l := time.Local
|
||||||
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
|
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
|
||||||
l = time.UTC
|
l = time.UTC
|
||||||
@ -475,6 +480,10 @@ func setSlice(vals []string, value reflect.Value, field reflect.StructField) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setTimeDuration(val string, value reflect.Value) error {
|
func setTimeDuration(val string, value reflect.Value) error {
|
||||||
|
if val == "" {
|
||||||
|
val = "0"
|
||||||
|
}
|
||||||
|
|
||||||
d, err := time.ParseDuration(val)
|
d, err := time.ParseDuration(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -226,7 +226,35 @@ func TestMappingTime(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bindTestData struct {
|
||||||
|
need any
|
||||||
|
got any
|
||||||
|
in map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingTimeUnixNano(t *testing.T) {
|
||||||
|
type needFixUnixNanoEmpty struct {
|
||||||
|
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
tests := []bindTestData{
|
||||||
|
{need: &needFixUnixNanoEmpty{}, got: &needFixUnixNanoEmpty{}, in: formSource{"createTime": []string{" "}}},
|
||||||
|
{need: &needFixUnixNanoEmpty{}, got: &needFixUnixNanoEmpty{}, in: formSource{"createTime": []string{}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range tests {
|
||||||
|
err := mapForm(v.got, v.in)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, v.need, v.got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMappingTimeDuration(t *testing.T) {
|
func TestMappingTimeDuration(t *testing.T) {
|
||||||
|
type needFixDurationEmpty struct {
|
||||||
|
Duration time.Duration `form:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
var s struct {
|
var s struct {
|
||||||
D time.Duration
|
D time.Duration
|
||||||
}
|
}
|
||||||
@ -236,6 +264,17 @@ func TestMappingTimeDuration(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 5*time.Second, s.D)
|
assert.Equal(t, 5*time.Second, s.D)
|
||||||
|
|
||||||
|
// ok
|
||||||
|
tests := []bindTestData{
|
||||||
|
{need: &needFixDurationEmpty{}, got: &needFixDurationEmpty{}, in: formSource{"duration": []string{" "}}},
|
||||||
|
{need: &needFixDurationEmpty{}, got: &needFixDurationEmpty{}, in: formSource{"duration": []string{}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range tests {
|
||||||
|
err := mapForm(v.got, v.in)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, v.need, v.got)
|
||||||
|
}
|
||||||
// error
|
// error
|
||||||
err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
|
err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user