[Go package] gin 的 validator 详解

948 阅读5分钟

一、介绍

validatorGin官方默认的验证器,基于结构和单个字段的tag标签实现值验证。

它具有以下独特功能:

  • 使用验证标记或自定义验证程序进行跨字段和跨结构验证。
  • Slice、Array 和 Map,允许验证多维字段的任何或所有级别。
  • 能够深入了解映射键和值以进行验证
  • 通过在验证之前确定类型接口的基础类型来处理类型接口。
  • 处理自定义字段类型,如 sql 驱动程序 Valuer 请参阅 Valuer
  • 别名验证标记,允许将多个验证映射到单个标记,以便更轻松地在结构上定义验证
  • 例如,提取自定义字段名称可以指定在验证时提取JSON名称,并在生成的FieldError中使用它
  • 可定制的 i18n 感知错误消息,即将错误信息按中文输出。

二、常用校验规则

标记标记说明
required必填Field或Struct validate:"required"
omitempty空时忽略Field或Struct validate:"omitempty"
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字符串值是否为有效的 JSONField validate:"json"
file符串值是否包含有效的文件路径,以及该文件是否存在于计算机上Field validate:"file"
url符串值是否包含有效的 urlField validate:"url"
uri符串值是否包含有效的 uriField 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"

三、在 Blog 项目中的具体运用

1、业务流程分析

在添加用户时校验数据,要求:

  1. 用户名必填,长度4-12位
  2. 密码必填,长度6-20位
  3. 角色码必须≥2,因为1表示管理员

2、添加Model的tag

type User struct {
   gorm.Model
   Username string `gorm:"type:varchar(20);not null" json:"username" validate:"required,min=4,max=12" label:"用户名"`
   Password string `gorm:"type:varchar(100);not null" json:"password" validate:"required,min=6,max=20" label:"密码"`
   Role     int    `gorm:"type:int;default:2" json:"role" validate:"required,gte=2" label:"角色码"` //validate 将 0 视作空值。所以修改为管理员:role=1;游客role=2
}

★3、在通用工具包utils/ 下新建 validator.go

package validator

import (
   "github.com/go-playground/locales/zh_Hans_CN"
   ut "github.com/go-playground/universal-translator"
   "github.com/go-playground/validator/v10"
   zhs "github.com/go-playground/validator/v10/translations/zh"
   "go-vue-blog/utils/errmsg"
   "reflect"
)

// Validator 传入待检验的对象实例。错误则返回汉化的错误信息,正确则返回空串
// 在添加用户时调用
func Validator(data interface{}) (string, int) {
   validate := validator.New()                 // 实例化验证器
   uni := ut.New(zh_Hans_CN.New())             // 设置成中文翻译器
   trans, _ := uni.GetTranslator("zh_Hans_CN") //获取翻译词典

   // 注册翻译器
   _ = zhs.RegisterDefaultTranslations(validate, trans)

   //当用户名出错时,返回的信息为:"Username长度不能超过12个字符" 希望字段也是中文:
   //1. 在 Model 的字段后tag添加label对应中文名
   //2. 使用 RegisterTagNameFunc() 获取并返回该中文
   validate.RegisterTagNameFunc(func(field reflect.StructField) string {
      label := field.Tag.Get("label")
      return label
   })

   //验证器验证
   `err := validate.Struct(data)`
   if err != nil {
      // err 类型断言成功,确实为 ValidationErrors 将值赋给 v
      for _, v := range err.(validator.ValidationErrors) {
         return v.Translate(trans), errmsg.ERROR
      }
   }
   return "", errmsg.SUCCESS
}

4、在API层的user.go的AddUser() 调用

// AddUser 添加用户
func AddUser(c *gin.Context) {

   ...
   
   msg, errCode := validator.Validator(&user)
   if errCode != errmsg.SUCCESS {...}
  
  ...
  
}