06-参数绑定

1、介绍

在 Gin 框架中参数不但能通过指定 key 接收,也可以直接绑定到结构体中

1.1、绑定方法概览

Gin 提供了 Must bindShould bind 两种类型的绑定方法,这两种类型对应的方法如下:

功能 Must bind方法 Should bind方法
Bind ShouldBind
绑定JSON BindJSON ShouldBindJSON
绑定XML BindXML ShouldBindXML
绑定GET BindQuery ShouldBindQuery
绑定YAML BindYAML ShouldBindYAML

a.MustBindWith 和 ShouldBindWith

Bind*类型的方法是对MustBindWith封装;Should*类型的方法是对ShouldBindWith的封装;

  • Must bind
    • Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
    • Behavior - 这些方法属于 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。 如果您希望更好地控制绑定,考虑使用 ShouldBind 等效方法。
  • Should bind
    • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    • Behavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。

1.2、绑定语法

定义被绑定的结构体

1
2
3
type StructName struct {
Xxx type `form:"paramName" binding:"required"`
}

标签说明:

  • form:"paramName": paramName为参数的名称;
  • binding:"required": 代表字段必须绑定;

2、绑定uri参数

通过使用函数BindQueryShouldBindQuery,用来只绑定GET请求中的uri参数,如:/funcName?a=x&b=x中的a和b

2.1、代码

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
33
34
35
36
37
38
39
40
package main
import (
"github.com/gin-gonic/gin" // 引入Gin框架
"go-use/practise" // 代码示例包
)
func main() {
engine := gin.Default()
TestBindQuery(engine)
_ = engine.Run()
}


// 定义结构体
type UriParam struct {
Name string `form:"name" binding:"required"`
Age int `form:"age"`
Home string `form:"home"`
}

// 绑定GET(BindQuery和ShouldBindQuery)
func TestBindQuery(engine *gin.Engine) {
engine.GET("/bindQuery", func(context *gin.Context) {
bindType := context.Query("type")
var uriParam UriParam
var err error
if bindType == "1" {
fmt.Println("BindQuery")
err = context.BindQuery(&uriParam)
} else {
fmt.Println("ShouldBindQuery")
err = context.ShouldBindQuery(&uriParam)
}
if err != nil {
context.JSON(500, gin.H{"error": err.Error()})
return
}
fmt.Printf("uriParam:%+v\n", uriParam)
context.JSON(200, gin.H{"result": uriParam})
})
}

2.2、请求

1
2
3
4
5
6
7
8
9
10
# 参数都传
[lepeng@centos ~]# curl -X GET http://127.0.0.1:8080/bindQuery?age=24&name=张三&home=北京&type=1
{"result":{"Name":"张三","Age":24,"Home":"北京"}}

[lepeng@centos ~]# curl -X GET http://127.0.0.1:8080/bindQuery?age=24&name=张三&home=北京&type=2
{"result":{"Name":"张三","Age":24,"Home":"北京"}}

# 必填参数name不填时,都会报错
[lepeng@centos ~]# curl -X GET http://127.0.0.1:8080/bindQuery?age=24&home=北京&type=2
{"error":"Key: 'UriParam.Name' Error:Field validation for 'Name' failed on the 'required' tag"}

3、绑定JSON

使用函数 BindJSONShouldBindJSON 来绑定提交的JSON参数信息。

3.1、代码

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
33
34
35
36
37
//-------- main.go ---------------
package main
import (
"github.com/gin-gonic/gin" // 引入Gin框架
"go-use/practise" // 代码示例包
)
func main() {
engine := gin.Default()
practise.TestBindJson(engine)
_ = engine.Run()
}
//------ go-use/practise/param_bind.go -------
// 定义待绑定的JSON结构体
type Param struct {
Name string `json:"name"`
Age int `json:"age"`
Likes []string `json:"likes"`
}
// 绑定提交的Json数据
func TestBindJson(engine *gin.Engine) {
engine.POST("/bindJson", func(context *gin.Context) {
var jsonParam Param
var err error
bindType := context.Query("type")
fmt.Println(bindType)
if bindType == "1" {
err = context.BindJSON(&jsonParam)
} else {
err = context.ShouldBindJSON(&jsonParam)
}
if err != nil {
context.JSON(500, gin.H{"error": err})
return
}
context.JSON(200, gin.H{"result": jsonParam})
})
}

3.2、请求

4、绑定XML

使用函数 BindXMLShouldBindXML 来绑定提交的 XML 参数信息。

4.1、代码

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
33
34
35
36
//-------- main.go ---------------
package main
import (
"github.com/gin-gonic/gin" // 引入Gin框架
"go-use/practise" // 代码示例包
)
func main() {
engine := gin.Default()
practise.TestBindXml(engine)
_ = engine.Run()
}
//------ go-use/practise/param_bind.go -------
// 修改待绑定的JSON结构体,添加XML标签
type Param struct {
Name string `json:"name" xml:"name"`
Age int `json:"age" xml:"age"`
Likes []string `json:"likes" xml:"likes"`
}
// 绑定Xml信息
func TestBindXml(engine *gin.Engine) {
engine.POST("/bindXml", func(context *gin.Context) {
var param Param
var err error
bindType := context.Query("type")
if bindType == "1" {
err = context.BindXML(&param)
} else {
err = context.ShouldBindXML(&param)
}
if err != nil {
context.JSON(500, gin.H{"error": err})
return
}
context.JSON(200, gin.H{"result": param})
})
}

4.2、请求

5、绑定request.Body

c.Request.Body 不能多次被调用,第一次绑定之后 c.Request.Body会设置成EOF

5.1、错误示例

a.代码

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
//-------- main.go ---------------
package main
import (
"github.com/gin-gonic/gin" // 引入Gin框架
"go-use/practise" // 代码示例包
)
func main() {
engine := gin.Default()
practise.TestBindXml(engine)
_ = engine.Run()
}
//------ go-use/practise/param_bind.go -------
type BodyAParam struct {
Name string `json:"name"`
}
type BodyBParam struct {
Home string `json:"home"`
}
// 重复绑定request.Body(错误示例)
func TestBindBody(engine *gin.Engine) {
engine.POST("/body", func(context *gin.Context) {
paramA := BodyAParam{}
paramB := BodyBParam{}
// c.ShouldBind 使用了 c.Request.Body,不可重用。
_ = context.ShouldBindJSON(&paramA)
// 因为现在 c.Request.Body 是 EOF,所以这里会报错。
_ = context.ShouldBindJSON(&paramB)
context.JSON(200,gin.H{"paramA":paramA,"paramB":paramB})
})
}

b.请求返回

1
2
3
# 发现只有paramA绑定成功
[lepeng@centos ~]# curl -X POST http://127.0.0.1:8080/body -d '{"name":"李四","home":"上海"}'
{"paramA":{"name":"李四"},"paramB":{"home":""}}

5.2、正确示例

a.代码

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
//-------- main.go ---------------
package main
import (
"github.com/gin-gonic/gin" // 引入Gin框架
"go-use/practise" // 代码示例包
)
func main() {
engine := gin.Default()
practise.TestBindXml(engine)
_ = engine.Run()
}
//------ go-use/practise/param_bind.go -------
type BodyAParam struct {
Name string `json:"name"`
}
type BodyBParam struct {
Home string `json:"home"`
}
// 重复绑定request.Body(正确示例)
func TestBindBody2(engine *gin.Engine) {
engine.POST("/body2", func(context *gin.Context) {
paramA := BodyAParam{}
paramB := BodyBParam{}
// 读取 c.Request.Body 并将结果存入上下文。
_ = context.ShouldBindBodyWith(&paramA,binding.JSON)
// 这时, 复用存储在上下文中的 body。
_ = context.ShouldBindBodyWith(&paramB,binding.JSON)
context.JSON(200,gin.H{"paramA":paramA,"paramB":paramB})
})
}

b.请求返回

1
2
3
# paramA和paramB都绑定成功
[lepeng@centos ~]# curl -X POST http://127.0.0.1:8080/body2 -d '{"name":"李四","home":"上海"}'
{"paramA":{"name":"李四"},"paramB":{"home":"上海"}}

06-参数绑定
https://flepeng.github.io/021-Go-34-框架-01-Gin-06-参数绑定/
作者
Lepeng
发布于
2024年11月14日
许可协议