整合validation库

Background

  • 利用validation库进行对参数校验,减少业务逻辑的手动校验

Get validation

  • go get github.com/go-playground/validator/v10

validation 配置项目

  • 逗号( ,):把多个验证标记隔开。注意:隔开逗号之间不能有空格, validate:”lt=0,gt=100”,逗号那里不能有空格,否则panic
  • 横线( - ):跳过该字段不验证
  • 竖线( | ):使用多个验证标记,但是只需满足其中一个即可
Tag Description Example
required 必填 Field或Struct validate:"required"
omitempty 空时忽略 And more
len 长度 Field validate:”len=0”
eq 等于 Field validate:”eq=0”
gt 大于 Field validate:”gt=0”
gte 大于等于 Field validate:”gte=0”
lt 小于 Field validate:”lt=0”
lte 小于等于 Field validate:”lte=0”
eqfield 同一结构体字段相等 Field validate:”eqfield=Field2”
nefield 同一结构体字段不相等 Field validate:”nefield=Field2”
gtfield 大于同一结构体字段 Field validate:”gtfield=Field2”
gtefield 大于等于同一结构体字段 Field validate:”gtefield=Field2”
ltfield 小于同一结构体字段 Field validate:”ltfield=Field2”
ltefield 小于等于同一结构体字段 Field validate:”ltefield=Field2”
eqcsfield 跨不同结构体字段相等 Struct1.Field validate:”eqcsfield=Struct2.Field2”
necsfield 跨不同结构体字段不相等 Struct1.Field validate:”necsfield=Struct2.Field2”
gtcsfield 大于跨不同结构体字段 Struct1.Field validate:”gtcsfield=Struct2.Field2”
gtecsfield 大于等于跨不同结构体字段 Struct1.Field validate:”gtecsfield=Struct2.Field2”
ltcsfield 小于跨不同结构体字段 Struct1.Field validate:”ltcsfield=Struct2.Field2”
ltecsfield 小于等于跨不同结构体字段 Struct1.Field validate:”ltecsfield=Struct2.Field2”
min 最小值 Field validate:”min=1”
max 最大值 Field validate:”max=2”
structonly 仅验证结构体,不验证任何结构体字段 Struct validate:”structonly”
nostructlevel 不运行任何结构级别的验证 Struct validate:”nostructlevel”
dive 向下延伸验证,多层向下需要多个dive标记 [][]string validate:”gt=0,dive,len=1,dive,required”
dive Keys & EndKeys 与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值 map[string]string validate:”gt=0,dive,keys,eq=1
required_with 其他字段其中一个不为空且当前字段不为空 Field validate:”required_with=Field1 Field2”
required_with_all 其他所有字段不为空且当前字段不为空 Field validate:”required_with_all=Field1 Field2”
required_without 其他字段其中一个为空且当前字段不为空 Field `validate:”required_without=Field1 Field2”
required_without_all 其他所有字段为空且当前字段不为空 Field validate:”required_without_all=Field1 Field2”
isdefault 是默认值 Field validate:”isdefault=0”
oneof 其中之一 Field validate:”oneof=5 7 9”
containsfield 字段包含另一个字段 Field validate:”containsfield=Field2”
excludesfield 字段不包含另一个字段 Field validate:”excludesfield=Field2”
unique 是否唯一,通常用于切片或结构体 Field validate:”unique”
alphanum 字符串值是否只包含 ASCII 字母数字字符 Field validate:”alphanum”
alphaunicode 字符串值是否只包含 unicode 字符 Field validate:”alphaunicode”
alphanumunicode 字符串值是否只包含 unicode 字母数字字符 Field validate:”alphanumunicode”
numeric 字符串值是否包含基本的数值 Field validate:”numeric”
hexadecimal 字符串值是否包含有效的十六进制 Field validate:”hexadecimal”
hexcolor 字符串值是否包含有效的十六进制颜色 Field validate:”hexcolor”
lowercase 符串值是否只包含小写字符 Field validate:”lowercase”
uppercase 符串值是否只包含大写字符 Field validate:”uppercase”
email 字符串值包含一个有效的电子邮件 Field validate:”email”
json 字符串值是否为有效的 JSON Field validate:”json”
file 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 Field validate:”file”
url 符串值是否包含有效的 url Field validate:”url”
uri 符串值是否包含有效的 uri Field validate:”uri”
base64 字符串值是否包含有效的 base64值 Field validate:”base64”
contains 字符串值包含子字符串值 Field validate:”contains=@”
containsany 字符串值包含子字符串值中的任何字符 Field validate:”containsany=abc”
containsrune 字符串值包含提供的特殊符号值 Field validate:”containsrune=☢”
excludes 字符串值不包含子字符串值 Field validate:”excludes=@”
excludesall 字符串值不包含任何子字符串值 Field validate:”excludesall=abc”
excludesrune 字符串值不包含提供的特殊符号值 Field validate:”containsrune=☢”
startswith 字符串以提供的字符串值开始 Field validate:”startswith=abc”
endswith 字符串以提供的字符串值结束 Field validate:”endswith=abc”
ip 字符串值是否包含有效的 IP 地址 Field validate:”ip”
ipv4 字符串值是否包含有效的 ipv4地址 Field validate:”ipv4”
datetime 字符串值是否包含有效的日期 Field validate:”datetime”

Configure the translator


// InitTrans 初始化翻译器
func InitTrans(locale string) (err error) {
// 修改gin框架中的Validator引擎属性,实现自定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {

// 注册一个获取json tag的自定义方法
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// 给SignUpParam注册自定义校验方法
v.RegisterStructValidation(SignUpParamStructLevelValidation,models.ParamSignUp{})

zhT := zh.New() // 中文翻译器
enT := en.New() // 英文翻译器

// 第一个参数是备用(fallback)的语言环境
// 后面的参数是应该支持的语言环境(支持多个)
// uni := ut.New(zhT, zhT) 也是可以的
uni := ut.New(enT, zhT, enT)

// locale 通常取决于 http 请求头的 'Accept-Language'
var ok bool
// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找
trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s) failed", locale)
}

// 注册翻译器
switch locale {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
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
}

How to Custom function

    1. 撰写自定义函数
    1. 在InitTrans 初始化翻译器中v.RegisterStructValidation(SignUpParamStructLevelValidation,models.ParamSignUp{})注册该函数
      // SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数
      func SignUpParamStructLevelValidation(sl validator.StructLevel) {
      su := sl.Current().Interface().(models.ParamSignUp)

      if su.Password != su.RePassword {
      // 输出错误提示信息,最后一个参数就是传递的param
      sl.ReportError(su.RePassword, "re_password", "RePassword", "eqfield", "password")
      }
      }

How to Use validation

errs,ok:=err.(validator.ValidationErrors)
if !ok{
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
return
}
c.JSON(http.StatusOK,gin.H{
"msg":errs.Translate(trans),
})

All Examle Code

package controller

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"
)

// 定义一个全局翻译器T
var trans ut.Translator

// InitTrans 初始化翻译器
func InitTrans(locale string) (err error) {
// 修改gin框架中的Validator引擎属性,实现自定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {

// 注册一个获取json tag的自定义方法
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// 给SignUpParam注册自定义校验方法
v.RegisterStructValidation(SignUpParamStructLevelValidation,models.ParamSignUp{})

zhT := zh.New() // 中文翻译器
enT := en.New() // 英文翻译器

// 第一个参数是备用(fallback)的语言环境
// 后面的参数是应该支持的语言环境(支持多个)
// uni := ut.New(zhT, zhT) 也是可以的
uni := ut.New(enT, zhT, enT)

// locale 通常取决于 http 请求头的 'Accept-Language'
var ok bool
// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找
trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s) failed", locale)
}

// 注册翻译器
switch locale {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
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
}

// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数
func SignUpParamStructLevelValidation(sl validator.StructLevel) {
su := sl.Current().Interface().(models.ParamSignUp)

if su.Password != su.RePassword {
// 输出错误提示信息,最后一个参数就是传递的param
sl.ReportError(su.RePassword, "re_password", "RePassword", "eqfield", "password")
}
}