在接口开发经常需要进行数据校验,validator包是一个比较强大的校验工具包。下面是一些学习总结,全文使用gin框架进行讲解,详细内容可以查看validator
下面我以“required”进行代码演示,要查看更多标记符可以查看官方文档
导包
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"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"
"net/http"
"reflect"
"regexp"
"strings"
)
定义全局键值对,可以方便取到验证器和翻译器,以及要翻译的语言
const (
ValidatorKey = "ValidatorKey"
TranslatorKey = "TranslatorKey"
locale = "zh"
)
初始化验证器和翻译器
func TransInit(c *gin.Context) {
//设置支持语言
chinese := zh.New()
english := en.New()
//设置国际化翻译器
uni := ut.New(chinese, chinese, english)
//设置验证器
val := validator.New()
//根据参数取翻译器实例
trans, _ := uni.GetTranslator(locale)
//翻译器注册到validator
switch locale {
case "chinese":
zhTranslations.RegisterDefaultTranslations(val, trans)
//使用fld.Tag.Get("comment")注册一个获取tag的自定义方法
val.RegisterTagNameFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("comment")
})
case "english":
enTranslations.RegisterDefaultTranslations(val, trans)
val.RegisterTagNameFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("en_comment")
})
}
c.Set(TranslatorKey, trans)
c.Set(ValidatorKey, val)
}
将路由变量params和gin框架绑定
func DefaultGetValidParams(c *gin.Context, params interface{}) error {
c.ShouldBind(params)
//获取验证器
val, _ := c.Get(ValidatorKey)
valid, _ := val.(*validator.Validate)
//获取翻译器
tran, _ := c.Get(TranslatorKey)
trans, _ := tran.(ut.Translator)
err := valid.Struct(params)
//如果数据效验不通过,则将所有err以切片形式输出
if err != nil {
errs := err.(validator.ValidationErrors)
sliceErrs := []string{}
for _, e := range errs {
//使用validator.ValidationErrors类型里的Translate方法进行翻译
sliceErrs = append(sliceErrs, e.Translate(trans))
}
return errors.New(strings.Join(sliceErrs, ","))
}
return nil
}
接下来我们定义一个结构体,对其传参检验输出结果
form是前端传入的参数名;comment是自定义tag,本文表示的是参数报错时显示的名称;validate里传入标记符,这里传入’required’(字段必填)
type login struct {
Username string ` form:"username" comment:"用户名" validate:"required"`
}
编写路由函数
func loginTest(c *gin.Context) {
TransInit(c)
req:=&login{}
err:=DefaultGetValidParams(c,req)
//失败时输出
if err != nil {
c.JSON(404, gin.H{
"code": 2000,
"err": err.Error(),
})
return
}
//成功时输出
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
"name":req.Username,
})
}
最后运行
func main() {
route := gin.Default()
route.POST("/login",loginTest)
route.Run(":8099")
}
使用Postman进行测试:
如果没填参数则提示“必填”:
如果把翻译的语言改为英语,效验失败时如下输出:
const (
ValidatorKey = "ValidatorKey"
TranslatorKey = "TranslatorKey"
//locale = "chinese"
locale = "english"
)
相比于官方定义好可以直接拿来用的标记符,自定义标记符具有更广泛的应用,我在项目中经常用的也是自定义标记符。同时自定义翻译器也可以更清楚的告诉用户格式输入错误。
首先自定义一个标识符"valid_username"
type login struct {
Username string ` form:"username" comment:"用户名" validate:"required,valid_username"`
}
这里我使用了正则进行效验。更多的正则匹配规则可自行百度,这里不作更多介绍
在初始化翻译器中加入如下函数:
//自定义验证方法
val.RegisterValidation("valid_username", func(fl validator.FieldLevel) bool {
matched, _ := regexp.Match("^[a-z]{6,30}$", []byte(fl.Field().String()))
return matched
})
//自定义翻译器
val.RegisterTranslation("valid_username", trans, func(ut ut.Translator) error {
return ut.Add("valid_username", "{0}输入格式不正确或长度不符", true)
},
func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("valid_username", fe.Field())
return t
})
即:
func TransInit(c *gin.Context) {
//设置支持语言
chinese := zh.New()
english := en.New()
//设置国际化翻译器
uni := ut.New(chinese, chinese, english)
//设置验证器
val := validator.New()
//根据参数取翻译器实例
trans, _ := uni.GetTranslator(locale)
//翻译器注册到validator
switch locale {
case "chinese":
zhTranslations.RegisterDefaultTranslations(val, trans)
val.RegisterTagNameFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("comment")
})
//自定义验证方法
val.RegisterValidation("valid_username", func(fl validator.FieldLevel) bool {
matched, _ := regexp.Match("^[a-z]{6,30}$", []byte(fl.Field().String()))
return matched
})
//自定义翻译器
val.RegisterTranslation("valid_username", trans, func(ut ut.Translator) error {
return ut.Add("valid_username", "{0}输入格式不正确或长度不符", true)
},
func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("valid_username", fe.Field())
return t
})
case "english":
enTranslations.RegisterDefaultTranslations(val, trans)
//使用fld.Tag.Get("en_comment")注册一个获取tag的自定义方法
val.RegisterTagNameFunc(func(fld reflect.StructField) string {
return fld.Tag.Get("en_comment")
})
}
c.Set(TranslatorKey, trans)
c.Set(ValidatorKey, val)
}