demo
gin框架使用github.com/go-playground/validator来进行参数校验,到目前为止已经支持github.com/go-playground/validator/v10,在定义结构体时可以使用 binding tag
标识相关校验规则.
-----------------models.verify---------------------
// 结构体中tag传入binding:"required",gin如果使用ShouldBind会自动校验参数,如果必须的字段没传入,就会返回错误
type Verify struct {
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required"`
Token int64 `json:"token" binding:"required"`
RepassWord string `json:"repass-word" binding:"required,eqfield=Password"` // 注意写法
}
----------------controlers.validatorTranslate------------
import (
"fmt"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
enTranslations "github.com/go-playground/validator/v10/translations/en"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
"reflect"
"strings"
)
// 定义一个全局翻译器变量trans
var trans ut.Translator
// InitTrans 初始化
func InitTrans(local string) (err error) {
// 修改gin框架中的Validator引擎属性,实现自定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// 注册一个获取json tag的自定义方法,这样注册后返回的错误信息,字段名就不是结构体的字段名字了而是json的
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
zhT := zh.New() // 中文翻译器
// 不需要英文的话可以不用传入
enT := en.New() // 英文翻译器
// 第一个参数是备用(fallback)的语言环境
// 后面的参数是应该支持的语言环境(支持多个)
// uni := ut.New(zhT, zhT) 也是可以的
uni := ut.New(enT, zhT, enT)
// local 通常取决于请求头的 'Accept-Language'
var ok bool
// 也可以使用 uni.FindTranslator(...) 传入多个local进行查找
trans, ok = uni.GetTranslator(local)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s) failed", local)
}
// 注册翻译器
switch local {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
return
}
--------------controlers.users----------------
// 下面是路由路径函数,r.GET("/signup", controlers.Signify)
func Signify(c *gin.Context) {
//var p models.Verify
p := new(models.Verify)
if err := c.ShouldBindJSON(&p); err != nil {
// 使用翻译,==先做类型判断==
if errs, ok := err.(validator.ValidationErrors); !ok {
fmt.Println("users: failed to translate:", err)
zap.L().Error("users: failed to translate:")
return
} else {
// trans 是在main中定义的全局变量,翻译器
// removeTopStruct 去除返回的错误信息中的结构体名称
c.JSON(http.StatusOK, gin.H{
"msg": "query string invalid",
"err": removeTopStruct(errs.Translate(trans)),
})
}
// 下面是未使用翻译
//data := gin.H{
// "msg": "query string invalid",
// "err": err.Error(),
//}
//c.JSON(http.StatusOK, data)
return
}
// 能走到下面说明起码参数是合法的,再做一些非空的判断
logic.SetUp(p)
// 返回响应
c.JSON(http.StatusOK, gin.H{
"msg": "success",
"valid_token": time.Now().UnixNano(), // 返回时间戳
})
return
}
// removeTopStruct 去除返回的错误信息中的结构体名称
func removeTopStruct(fields map[string]string) map[string]string {
res := map[string]string{}
for field, err := range fields {
res[field[strings.Index(field, ".")+1:]] = err
}
return res
}
-----------------main.go--------------------
// 初始化gin框架使用的校验器翻译器
if err := controlers.InitTrans("zh"); err != nil {
fmt.Println("main: init translate function failed:", err.Error())
zap.L().Error("main: init translate function failed:", zap.Error(err))
return
}
基本使用
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Verify struct {
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required"`
Token int64 `json:"token" binding:"required"`
RepassWord string `json:"repass-word" binding:"required,eqfield=Password"` // 注意写法
}
func main() {
r := gin.Default()
r.POST("/signup", func(c *gin.Context) {
var p Verify
if err := c.ShouldBind(&u); err != nil {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
return
}
// 保存入库等业务
// ...........................
c.JSON(http.StatusOK, "success")
})
_ = r.Run(":8081")
}
code view
// 获取json tag的自定义方法,这样错误信息返回的就不是结构体名称,而是json里tag名
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
可以看到返回的错误信息里, Verify.repass-word,包含结构体名称,可以去掉,剽窃的文档issue
里的方法
https://github.com/go-playground/validator/issues/633#issuecomment-654382345提供的方法
// 去除返回的错误信息中的结构体名称
func removeTopStruct(fields map[string]string) map[string]string {
res := map[string]string{}
for field, err := range fields {
res[field[strings.Index(field, ".")+1:]] = err
}
return res
}
--------------------------------------------
// 调用方法
c.JSON(http.StatusOK, gin.H{
"msg": "query string invalid",
"err": removeTopStruct(errs.Translate(trans)),
})