Gin框架学习记录 - 参数绑定
hanpy

单独接收参数应该在实际的项目中用的不多,因为灵活性比较差,而且不容易扩展,用到的更多的应该是参数绑定。

Gin提供了Must bind 和 Should bind两种类型的绑定方法。Bind*类型的方法是对MustBindWith封装;Should*类型的方法是对ShouldBindWith的封装。

  1. Must bind

    方法Bind, BindJSON, BindXML, BindQuery, BindYAML
    行为:些方法属于 MustBindWith 的具体调用。 如果发生绑定错误,则请求终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。响应状态码被设置为 400 并且 Content-Type 被设置为 text/plain; charset=utf-8。 如果您在此之后尝试设置响应状态码,Gin 会输出日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422

  2. Should bind

    方法ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    行为:这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求

绑定GET参数

分别使用BindQueryShouldBindQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
type User struct {
Name string `json:"name" form:"name" binding:"required"` // 必填
Age int `json:"age" form:"age" binding:"required"` // 必填
Home string `json:"home" form:"home" binding:"required"` // 必填
}

func Bind(r *gin.Engine) {
bindRoute := r.Group("/bindGet")
{
// BindQuery
bindRoute.GET("/getMust", func(ctx *gin.Context) {
user := &User{}
err := ctx.BindQuery(user)
if err != nil {
ctx.JSON(200, gin.H{"error": fmt.Sprintln(err)})
return
}
ctx.JSON(200, gin.H{"user": user})
})

// ShouldBindQuery
bindRoute.GET("/getShould", func(ctx *gin.Context) {
user := &User{}
err := ctx.ShouldBindQuery(user)
if err != nil {
ctx.JSON(200, gin.H{"error": fmt.Sprintln(err)})
return
}
ctx.JSON(200, gin.H{"user": user})
})
}
}

使用BindQuery的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#1 三个字段都是必须要传的,只传一个会输出错误
$ curl "http://127.0.0.1:9090/bindGet/getMust?home=beijing"
{"error":"Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag\n"}%

#2 同上一样的请求,看一下头信息
$ curl -i "http://127.0.0.1:9090/bindGet/getMust?home=beijing"
HTTP/1.1 400 Bad Request
Date: Sun, 26 Mar 2023 12:32:45 GMT
Content-Length: 172
Content-Type: text/plain; charset=utf-8

{"error":"Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag\n"}

#3 控制台输出
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200
[GIN] 2023/03/26 - 20:32:45 | 200 | 87.344µs | 127.0.0.1 | GET "/bindGet/getMust?home=beijing"

#4 正常绑定情况
$ curl "http://127.0.0.1:9090/bindGet/getMust?home=beijing&name=hanpy&age=30"
{"user":{"name":"hanpy","age":30,"home":"beijing"}}

从上面的信息可以看出,使用BindQuery(Must Bind方式)发生错误响应头状态码会设置成400(Bad Request/错误请求),并且在控制台也能看到响应的提示信息

使用ShouldBindQuery的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1 三个字段都是必须要传的,只传一个会输出错误
$ curl "http://127.0.0.1:9090/bindGet/getShould?home=beijing"
{"error":"Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag\n"}%

#2 同上一样的请求,看一下头信息
$ curl -i "http://127.0.0.1:9090/bindGet/getShould?home=beijing"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 26 Mar 2023 12:56:13 GMT
Content-Length: 172

{"error":"Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag\n"}

#3 正常绑定情况
$ curl "http://127.0.0.1:9090/bindGet/getShould?home=beijing&name=hanpy&age=30"
{"user":{"name":"hanpy","age":30,"home":"beijing"}}

绑定POST参数

绑定POST数据主要是使用 BindWith()ShouldBindWith()

1
2
3
4
5
6
7
8
9
10
11
// 绑定POST请求数据
r.POST("/bind/post", func(ctx *gin.Context) {
user := &User{}
// ctx.BindWith(user, binding.FormPost)
err := ctx.ShouldBindWith(user, binding.FormPost)
if err != nil {
ctx.JSON(200, gin.H{"error": fmt.Sprintln(err)})
return
}
ctx.JSON(200, gin.H{"user": user})
})
1
2
3
4
5
6
7
#1 绑定验证失败
$ curl -d "home=beijing&name=hanpy" "http://127.0.0.1:9090/bind/post"
{"error":"Key: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag\n"}

#2 绑定成功
$ curl -d "home=beijing&name=hanpy&age=30" "http://127.0.0.1:9090/bind/post"
{"user":{"name":"hanpy","age":30,"home":"beijing"}}

绑定Json数据

主要使用 BindJSON()ShouldBindJSON()

1
2
3
4
5
6
7
8
9
10
r.POST("/bind/json", func(ctx *gin.Context) {
user := &UserJson{}
//ctx.BindJSON()
err := ctx.ShouldBindJSON(user)
if err != nil {
ctx.JSON(200, gin.H{"error": fmt.Sprintln(err)})
return
}
ctx.JSON(200, gin.H{"user": user})
})
1
2
3
4
5
6
7
8
#1 
$ curl -H 'Content-Type: application/json' --url http://127.0.0.1:9090/bind/json --data '{
"name": "hanpy",
"likes":["eat", "sleep"]
}'

#1 返回数据
{"user":{"name":"hanpy","likes":["eat","sleep"]}}

绑定Header数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Header struct {
ContentType string `header:"Content-Type"`
UserAgenet string `header:"user-agent"`
}

r.POST("/bind/header", func(ctx *gin.Context) {
header := &Header{}
err := ctx.ShouldBindHeader(header)
if err != nil {
ctx.JSON(200, gin.H{"error": fmt.Sprintln(err)})
return
}
ctx.JSON(200, gin.H{"header": header})
})
1
2
3
4
5
6
7
8
9
10
#1 访问数据
$ curl --request "POST" -H "content-type:application/json" -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50" --url "http://127.0.0.1:9090/bind/header"

#1 返回数据
{
"header": {
"ContentType": "application/json",
"UserAgenet": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
}
}

通过上面的列子得出一个结论,绑定 header 数据的时候大小写是不敏感的。