【笔记】Go语言的Validator

前言

Go语言通过Validator实现结构体的字段值验证,在实例化结构体变量时只有所有字段的值都符合校验规则才能被成功实例化

下载依赖

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

初始化

validate.SetTagName(""):设置标签名

validate:缺省值,标签名

main.go
1
2
3
4
5
6
7
8
9
10
package main

import "github.com/go-playground/validator/v10"

var validate *validator

func init() {
validate = validator.New()
validate.SetTagName("validate")
}

标签分隔符

,:需要通过多个验证
|:只需要通过一种验证
-:跳过该字段的验证

字符串校验

alpha:字母
alphanum:字母和数字
alphaunicode:字母和Unicode字符(包含中文)
alphanumunicode:字母和数字和Unicode字符(包含中文)
ascii:ASCII字符
contains=字符串:包含指定字符串
excludes=字符串:不包含指定字符串
startswith=字符串:以指定字符串开头
endswith=字符串:以指定字符串结尾
startsnowith=字符串:不以指定字符串开头
endsnowith=字符串:不以指定字符串结尾
lowercase:字符串中的字母全是小写
uppercase:字符串中的字母全是大写
boolean:布尔值字符串
multibyte:多字节字符串
number:整型数值字符串
numeric:整型数值和浮点型数值字符串

格式化字符串校验

base64:Base64格式字符串
base64url:Base64格式URL字符串
credit_card:信用卡号字符串
datetime=2006-01-02 15:04:05:指定日期格式的字符串
e164:包含国家区号的手机号格式字符串,如:+8613800000000
email:电子邮件格式字符串
hexadecimal:16进制字符串
json:JSON格式字符串
jwt:JWT格式字符串
longitude:经度格式字符串
latitude:纬度格式字符串
timezone:时区格式字符串,如:Asia/Shanghai
iscolor:颜色字符串
country_code:国家代码字符串

网络地址校验

fqdn:域名
hostname:主机名
hostname_port:主机名和端口号
ip:IPv4地址和IPv6地址
ipv4:IPv4地址
ipv6:IPv6地址
mac:MAC地址
uri:统一资源标识符
url:统一资源定位符
url_encode:URL编码的统一资源定位符

数值比较校验

eq=0:等于
gt=0:大于
lt=0:小于
get=0:大于等于
let=0:小于等于
ne=0:不等于

必填校验

必填

  • 必须不为零值

required:必填
required_if=字段名 值:当指定字段为指定值时,必填
required_unless=字段名 值:当指定字段不为指定值时,必填
required_with=字段名:当指定字段的值不为零值时,必填
required_with_all=字段名1 字段名2:当指定多个字段的值都不为零值时,必填
required_without=字段名:当指定字段为零值时,必填
required_without_all=字段名1 字段名2:当指定多个字段都为零值时,必填

必不填

  • 必须为零值

excluded_with=字段名:当指定字段的值不为零值时,必不填
excluded_with_all=字段名1 字段名2:当指定多个字段的值都不为零值时,必不填
excluded_without=字段名:当指定字段为零值时,必不填
excluded_without_all=字段名1 字段名2:当指定多个字段都为零值时,必不填

目录文件路径校验

dir:目录路径
file:文件路径

其他校验

isdefault:零值
len:字符串长度
max:最大值,可用于数值、字符串、切片、集合
min:最小值,可用于数值、字符串、切片、集合
oneof=<value_1> <value_2>:所有枚举的值的其中之一
omitempty:如果没有值,就跳过该字段的验证
dive:深入一层验证,通常在切片类型字段或映射类型字段上使用

相对于其他字段校验

相对于其他基本类型的字段校验

eqfield=字段名:等于当前结构体的指定字段值
gtfield=字段名:大于当前结构体的指定字段值
ltfield=字段名:小于当前结构体的指定字段值
gtefield=字段名:大于等于当前结构体的指定字段值
ltefield=字段名:小于等于当前结构体的指定字段值
nefield=字段名:不等于当前结构体的指定字段值
fieldcontains=字段名1 字段名2:包含在当前结构体的指定多个字段值
fieldexcludes=字段名1 字段名2:不包含在当前结构体的指定多个字段值

相对于其他结构体类型的字段校验

eqcsfield=字段名.字段名:等于子结构体的指定字段值
gtcsfield=字段名.字段名:大于子结构体的指定字段值
ltcsfield=字段名.字段名:小于子结构体的指定字段值
gtecsfield=字段名.字段名:大于等于子结构体的指定字段值
ltecsfield=字段名.字段名:小于等于子结构体的指定字段值
necsfield=字段名.字段名:不等于子结构体的指定字段值

在结构体上定义校验标签

min=1:切片的最小长度
min=2:每个字符串的最小长度

1
2
3
4
5
6
package model

type 结构体名 struct {
字段名 string `validate:"required,alpha"`
字段名 []string `validate:"required,min=1,dive,required,alpha,min=2"`
}

自定义字段类型的校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type 结构体名 struct {
字段名 自定义的类型1
字段名 自定义的类型2
}

func main() {
validate.RegisterCustomTypeFunc(函数名, 自定义的类型1, 自定义的类型2)
var 结构体变量名 = 结构体名{}
err := validate.Struct(结构体变量名)
}

func 函数名(field reflect.Value) interface{} {
if value, ok := field.Interface().(断言该字段原本的类型); ok {
res, err := value.Value()
if err == nil {
// 返回字段类型
return res
}
}
// 返回空指针
return nil
}

自定义结构体的校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type 结构体名 struct {}

func main() {
var 结构体变量名 = 结构体名{}
validate.RegisterStructValidation(函数名, 结构体变量名)
err := validate.Struct(结构体变量名)
}

func 函数名(structLevel validator.StructLevel) {
结构体变量名 := structLevel.Current().Interface().(结构体名)
if 判断条件 {
// 校验不通过
structLevel.ReportError(校验有误的字段, "字段名", "结构体字段名", "标签", "")
}
}

自定义标签的校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type 结构体名 struct {
字段名 string `validate:"自定义标签"`
}

func main() {
validate.RegisterValidation("自定义标签", 函数名)
var 结构体变量名 = 结构体名{}
err := validate.Struct(结构体变量名)
}

func 函数名(fieldLevel validator.FieldLevel) bool {
value := fieldLevel.Field().string()
if 判断条件 {
// 验证不通过
return false
}
// 验证通过
return true
}

Map类型数据验证

  • 定义一个与原数据相同结构的Map类型数据作为验证规则
1
2
3
4
5
func main() {
变量名 := map[string]interface{}{"key1": "value", "key2": "value"}
rules := map[string]interface{}{"key1": "required", "key2": "required"}
err := validate.ValidateMap(变量名, rules)
}
  • 如果Map中存储了切片,只需要指定一次验证规则即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
变量名 := map[string]interface{}{
"key1": "value",
"key2": []map[string]interface{}{
{
"key3": "value",
},
{
"key3": "value",
},
}
}
rules := map[string]interface{}{
"key1": "value",
"key2": map[string]interface{}{
"key3": "required",
}
}
err := validate.ValidateMap(变量名, rules)
}

通过Map类型的规则验证结构体

1
2
3
4
5
6
7
8
9
10
type 结构体名 struct {
字段名 string
}

func main() {
结构体变量名 := 结构体名{字段名: 字段值}
rules := map[string]interface{}{"字段名": "required"}
validate.RegisterStructValidationMapRules(rules, 结构体名{})
err := validate.Struct(结构体变量名)
}

完成

参考文献

哔哩哔哩——Linux-后端攻城狮