大家好,我是 frank。「Golang语言开发栈」公众号作者。
01 、介绍
在使用 Gin 框架开发项目时,通常我们选择模型绑定的方式接收请求参数,我们在上一遍文章中,已经介绍过使用 Gin 框架接收请求参数的常用方式。
本文我们主要介绍怎么验证绑定到结构体的字段,顺便补充关于模型绑定的一些内容。
02 、模型绑定
关于 Gin 框架的模型绑定,我们在上一篇文章中介绍了 ShouldBind
方法,该方式也是我们在使用 Gin 框架开发项目时,最常使用的方式。
一般使用场景
示例代码:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/login", func(c *gin.Context) {
var login Login
if err := c.ShouldBind(&login); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"data": login,
})
})
r.Run()
}
type Login struct {
User string `form:"user"`
Password string `form:"password"`
}
输出结果:
curl -s -X GET http://127.0.0.1:8080/login\?user\=frank\&password\=123456 | jq
{
"data": {
"User": "frank",
"Password": "123456"
}
}
阅读上面这段代码,我们使用 GET 请求方式,需要给结构体中的字段,添加 tag form
。
需要注意的是,当我们使用 ShouldBind
方式时,如果使用 GET 请求方式,Gin 框架只会使用 form
标签;
如果使用 POST 请求方式,Gin 框架首先检查 content-type
的值是否是 JSON
或 XML
,若是,则使用 json
或 xml
标签,若不是,则再使用 form
标签。
特殊使用场景
示例代码:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var login Login
var register Register
if err := c.ShouldBind(&login); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
if err := c.ShouldBind(®ister); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"data": login,
"data2": register,
})
})
r.Run()
}
type Login struct {
User string `form:"user" json:"user"`
Password string `form:"password" json:"password"`
}
type Register struct {
User string `form:"user" json:"user"`
Password string `form:"password" json:"password"`
}
输出结果:
curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"frank", "password": "123456"}' | jq
{
"error": "EOF"
}
阅读上面这段代码,将同一次请求,绑定到多个结构体,我们使用 ShouldBind
方式,得到的输出结果是 EOF
,这是因为 ShouldBind
使用了 Request.Body
,它不可以重用。
当使用一次 ShouldBind
之后,Request.Body
的值是 EOF
,再次使用 ShoudBind
就会返回错误。
我们可以使用 ShoudBindBodyWith
解决该问题,ShouldBindBodyWith
在绑定之前会将 body 存储到上下文中。
我们只需要修改上面这段代码,即可实现多次绑定,示例代码:
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var login Login
var register Register
if err := c.ShouldBindBodyWith(&login, binding.JSON); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
if err := c.ShouldBindBodyWith(®ister, binding.JSON); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"data": login,
"data2": register,
})
})
r.Run()
}
需要注意的是,该方式会影响性能,所以尽量避免需要多次绑定的使用场景。
还有就是只有 JSON
、XML
、MsgPack
、ProtoBuf
使用 ShouldBind
多次绑定,会出现该问题。其它格式,可以使用 ShouldBind
多次绑定,并且不会影响性能。
03 、验证
接下来,我们介绍 Gin 框架绑定到结构体的字段的验证方式。
Gin 框架提供了 2 种绑定方式,一种是我们已经介绍的 ShouldBind*
,该方式是 ShouldBindWith*
的快捷方式。ShouldBind*
和 ShouldBindWith*
方式可以返回错误。
另一种是 Bind*
,该方式是 MustBindWith*
的快捷方式。该方式不可以返回错误,也就是如果发生绑定错误,则请求终止。我们一般很少使用该方式。
我们使用 ShouldBind*
方式为例,介绍怎么验证绑定到结构体的字段。
标签验证(字段级验证)
示例代码:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var login Login
if err := c.ShouldBind(&login); err != nil {
c.JSON(200, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"data": login,
})
})
r.Run()
}
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password"`
}
输出结果:
curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"", "password": "123456"}' | jq
{
"error": "Key: 'Login.User' Error:Field validation for 'User' failed on the 'required' tag"
}
curl -s -X POST http://127.0.0.1:8080/login -H 'content-type: application/json' -d '{"user":"frank", "password": "123456"}' | jq
{
"error": "Key: 'Login.User' Error:Field validation for 'User' failed on the 'len' tag"
}
阅读上面这段代码,我们在结构体 Login
的字段 User
标签中,新增 binding:"required,len=10"
,请求参数中,故意在请求时将 user
的值设置为空字符串和长度不等于 10 的字符串,返回结果给出了验证错误的信息。
实际上,Gin 框架使用 github.com/go-playground/validator/v10
进行验证。
除了 required
和 len
之外,还有很多属性,读者朋友们可以阅读 Validator 文档[1]。
04 、总结
本文我们介绍 Gin 框架怎么验证绑定到结构体的字段,分为字段级验证(标签验证)和结构体级验证,限于篇幅,本文我们先只介绍字段级验证。
Gin 框架中的验证,使用的是三方库 validator
,读者朋友们可以阅读其官方文档,了解更多使用方式。
参考资料
[1]
Validator 文档: https://pkg.go.dev/github.com/go-playground/validator/v10