Gin表单绑定验证器
Mon, Jul 13, 2020
阅读时间 2 分钟
gin中内置validator的基础使用
type UserLoginParam struct {
Name string `form:"name" json:"name" binding:"required,min=2,max=30"`
Password string `form:"password" json:"password" binding:"required,min=8,max=40"`
}
func TestValidator(t *testing.T) {
app := gin.New()
app.POST("/login", func(context *gin.Context) {
var userLoginService UserLoginParam
err := context.ShouldBind(&userLoginService)
if err != nil {
context.JSON(http.StatusUnprocessableEntity, gin.H{
"msg": err.Error(),
})
}else{
context.JSON(http.StatusOK, gin.H{
"msg": "通过验证",
})
}
return
})
_ = app.Run(":8888")
}
我们使用postman请求测试下:
拒绝:
通过:
可以看到表单验证已经生效,但是错误提示的字段不是特别友好,我们首先需要整理成前端能够解析的样子,在考虑翻译的问题。
优化返回格式
我们这里可以看到所有的验证输出都是来之或者ShouldBind后的err,我们就先看看这个err在哪里定义的,这里可以通过fmt包查看他的类型,就知道他定义的位置。
fmt.Printf("%T \n", err)
这里发现他的类型为:
下一步就是找到他的定义了。然后发现他是一个FieldError切片类型:
然后我们继续看看FieldError是个什么东西:
发现这里的FieldError是一个接口类型,里面的接口想必就是获取各种信息的。我们打印一下看看,所有我们需要改一下我们的代码,这里我就随便打印几个,其实我们看下官方在接口定义哪里的注释,也大概能明白什么意思。
if err != nil {
fmt.Printf("%T \n", err)
errors := err.(validator.ValidationErrors)
for _, value := range errors {
fmt.Println(value.Kind())
fmt.Println(value.Field())
fmt.Println(value.ActualTag())
}
context.JSON(http.StatusUnprocessableEntity, gin.H{
"msg": err.Error(),
})
} else {
context.JSON(http.StatusOK, gin.H{
"msg": "通过验证",
})
}
访问接口=>输出
这里也就很明显了。我们现在就能拿到我们错误信息进行封装返回了。我们小改一下代码:
func getParamError(err validator.ValidationErrors) map[string]string {
result := make(map[string]string, 0)
for _, v := range err {
result[v.Field()] = v.Tag()
}
return result
}
func TestValidator(t *testing.T) {
app := gin.Default()
app.POST("/login", func(context *gin.Context) {
var userLoginService UserLoginParam
err := context.ShouldBind(&userLoginService)
if err != nil {
fmt.Printf("%T \n", err)
errors := err.(validator.ValidationErrors)
context.JSON(http.StatusUnprocessableEntity, gin.H{
"msg": getParamError(errors),
})
} else {
context.JSON(http.StatusOK, gin.H{
"msg": "通过验证",
})
}
return
})
_ = app.Run(":8888")
}
现在访问一下再看看:
但是这样对于前端,还是不好获取,所有我把他改为数组。前端只需要循环这个数组就行。
自定义字段校验方法
type UserLoginParam struct {
Name string `form:"name" json:"name" binding:"required,min=2,max=30,bookableDate"`
Password string `form:"password" json:"password" binding:"required,min=8,max=40"`
}
func getParamError(err validator.ValidationErrors) []string {
result := make([]string, 0)
for _, v := range err {
result = append(result, fmt.Sprintf("%v错误:%v", v.Field(), v.Tag()))
}
return result
}
// customFunc 自定义字段级别校验方法
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}
func TestValidator(t *testing.T) {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
//注册
_ = v.RegisterValidation("bookableDate", bookableDate)
}
app := gin.Default()
app.POST("/login", func(context *gin.Context) {
var userLoginService UserLoginParam
err := context.ShouldBind(&userLoginService)
if err != nil {
errors := err.(validator.ValidationErrors)
context.JSON(http.StatusUnprocessableEntity, gin.H{
"msg": getParamError(errors),
})
} else {
context.JSON(http.StatusOK, gin.H{
"msg": "通过验证",
})
}
return
})
_ = app.Run(":8888")
}
参考连接:https://gin-gonic.com/zh-cn/docs/examples/custom-validators/