问题背景
项目中使用gin框架,post请求之类的用json传参,过程中遇到的一个问题是:如果给字段binding一个required必填,那么这个字段就没办法传如对应数据类型的零值。同时对应的另一个问题是,如果不是required的字段,传参校验无法确定传入参数是数据类型的零值还是根本没传这个字段。
具体情况可以参考这个demo:
package main
import (
// "fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password int `form:"password" json:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
c.JSON(http.StatusOK, gin.H{"status": "success", "password": json.Password, "user": json.User})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
router.Run(":8080")
}
结果:
这和不传这两个字段效果一样:
同理可以看这组非required的对比:
package main
import (
// "fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Login struct {
User string `form:"user" json:"user"`
Password int `form:"password" json:"password"`
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
c.JSON(http.StatusOK, gin.H{"status": "success", "password": json.Password, "user": json.User})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
router.Run(":8080")
}
可以看到两种情况无法分辨:
问题分析
一番百度了解发现,gin的参数校验是用的go-playground/validator
,然后validator的官方文档里就讲了,required是要求必须是非零值,但业务上是会有需要必填的字段传入零值的,这就造成了这个问题。
解决方法
又一番百度,提到一个参数数据类型定成对应的指针类型,像这样:
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Login struct {
//required的参数用指针即可传入对应数据类型的零值
//非required的参数,若请求不传这个字段,则对应指针值为nil
User *string `form:"user" json:"user" binding:"required"`
Password *int `form:"password" json:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
c.JSON(http.StatusOK, gin.H{"status": "success", "password": json.Password, "user": json.User})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
router.Run(":8080")
}
经过测试,确实有效,对比图:
同理,非required也可以通过判断是否为nil区分是否传参:
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Login struct {
//required的参数用指针即可传入对应数据类型的零值
//非required的参数,若请求不传这个字段,则对应指针值为nil
User *string `form:"user" json:"user"`
Password *int `form:"password" json:"password"`
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
if json.Password == nil {
fmt.Println("no password")
}
if json.User == nil {
fmt.Println("no user")
}
c.JSON(http.StatusOK, gin.H{"status": "success", "password": json.Password, "user": json.User})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
router.Run(":8080")
}
以上