绑定器
在上一篇文章中我提到了如何将如 json 这样的数据作为响应返回给前端,这一篇文章则主要讲述当服务器需要接收前端来发的信息时,该如何使用 gin 框架来对付此事。
在 gin 中,主要使用bind来解析前端发来的数据,其方法是将数据解析为后端代码中的结构体,以及在其过程中进行验证,如果绑定失败,会报错之类的。由于其行为相当于将数据与结构体相绑定,所以叫绑定器。
绑定方式
gin 一共提供了两个绑定函数,一个是MustBind不推荐使用,因为如果绑定失败,那么一个是ShouldBind。MustBind不推荐使用,因为如果绑定失败,那么MustBind会直接作废这条前端请求,方式是修改请求的 http 状态码。更好用的是ShouldBind函数,当他绑定失败的话,该函数会返回错误信息。所以接下来只讲ShouldBind函数。
ShouldBind函数有几种细分函数,分别用来对应相对应的前端发送信息的方式,即直接发送json、query参数、uri动态参数。
json
第一种先讲 JSON 其他方式就只是单词变一变。
这里需要引入一个概念,就是 go语言的 tag 语法,一般都是跟在结构体的属性后面,用来在具体函数调用结构体中,对于结构体某个具体的属性进行定制化的操作,类似于 java 的注解与 c# 的特性,主要是初始化和特殊处理之类的操作。
首先需要先定义用来绑定的结构体:
type Info struct{
Name string `json:"user"`
Message string `json:"msg"`
Number int `json:"number"`
}
然后就是使用绑定函数:
router.POST("/", func(c *gin.Context) {
var info Info
err := c.ShouldBindJSON(&info)
if err != nil {
c.JSON(http.Status.OK, gin.H{"msg": "数据有问题"})
return
}
c.JSON(http.Status.OK, info)
})
与接收 json 数据相同的还有 yaml 、xml 的函数。
query
接下来是绑定 query 参数。如果想让结构体与query绑定,需要为结构体属性加上from tag 。如下:
type Info struct{
Name string `json:"user" from:"name"`
Message string `json:"msg" from:"msg"`
Number int `json:"number" from:"number"`
}
要养成在属性加上from tag 的好习惯,否则出现无法解析的情况可能会忘记排查这个地方而导致找不到问题。
上述代码中 tag 里有json单纯只是为了在下面代码中使用JSON函数来返回接收到的数据,不是必须的哦。
然后就是使用绑定函数:
router.POST("/", func(c *gin.Context) {
var info Info
err := c.ShouldBindQuery(&info)
if err != nil {
c.JSON(http.Status.OK, gin.H{"msg": "数据有问题"})
return
}
c.JSON(http.Status.OK, info)
})
uri
接下来是动态参数,使用uri tag。如下:
type Info struct{
Name string `json:"user" uri:"name"`
Message string `json:"msg" uri:"msg"`
Number int `json:"number" uri:"number"`
}
然后是使用绑定函数:
router.POST("/", func(c *gin.Context) {
var info Info
err := c.ShouldBindUri(&info)
if err != nil {
c.JSON(http.Status.OK, gin.H{"msg": "数据有问题"})
return
}
c.JSON(http.Status.OK, info)
})
HTTP content-type与其他
这个类型也是使用 from tag 。不过使用的是ShouldBind函数。
type Info struct{
Name string `json:"user" from:"name"`
Message string `json:"msg" from:"msg"`
Number int `json:"number" from:"number"`
}
router.POST("/", func(c *gin.Context) {
var info Info
err := c.ShouldBind(&info)
if err != nil {
c.JSON(http.Status.OK, gin.H{"msg": "数据有问题"})
return
}
c.JSON(http.Status.OK, info)
}
这个数据类型是包含在请求头中的,具体格式应该是在HTTP协议中学习,如果不知道可以上网百度一下,这种格式我本人不太常用。
bind自带的验证器
因为这个功能大量使用到了 tag 语法,所以绑定器也基于 tag 语法提供了很多自带的数据验证器,包括:必须数据、长度限制、可忽略、不大于等。使用方式是在 tag 中添加binding tag。
如下:
type Info struct{
Name string `json:"user" binding:"required"`
Message string `json:"msg" binding:""len=1024`
Number int `json:"number" binding:"-"`
}
这样 name 属性是必须要能绑定到数据,而 message 绑定到的数据要求长度为1024,而 number 属性则会被忽略。
我列举了一些验证器,可以看一看:
binding:"required" //必须字段
binding:"max=5"//字符串最长不超过5
binding:"min=5"//字符串最短不少于5
binding:"len=5"//字符串长度为5
binding:"contain=123"//字符串包含123
binding:"excludes=123"//字符串不包含123
binding:"startwith=123"//字符串前缀为123
binding:"endwith=123"//字符串后缀为123
binding:"eq=5"//数字等于5
//对于数字分别还有:
ne//不等于
gt//大于
gte//大于等于
lt//小于
lte//小于等于
binding:"eqfield=PassWord"//与同级字段PassWord比较,要一致
binging:"nefield=PassWord"//与同级字段PassWord比较,要不一样
binging:"-"//忽略该字段
binging:"oneof=a b"//该字段为枚举型,只能有a或b两个数据
验证器自定义报错信息
在验证器报错后, gin 也允许输出自定义的错误信息,这需要msg tag。
type Info struct{
Name string `json:"user" binding:"required" msg:"缺少用户名"`
Message string `json:"msg" binding:""len=1024`
Number int `json:"number" binding:"-"`
}
这样当Name属性无法绑定到数据中的字段后,就会输出报错“缺少用户名”。