一、Go Validator 简介
1.1 Go Validator 简介
Go Validator是一个开源的数据验证库,为 Go 结构体提供强大且易于使用的数据验证功能。该库允许开发者为其数据结构定义自定义验证规则,并确保传入的数据满足指定的条件。Go Validator支持 内置验证器、自定义验证器,甚至允许链式多个验证规则以满足更复杂的数据验证需求。
Go Validator的主要特点:
- 内置验证器: Go Validator内置了多个验证器,例如 email、URL、IPv4、IPv6 等,这些验证器可以直接用于常见的验证场景;
- 自定义验证器:如果内置验证器无法满足验证需求,还可以通过定义自己的验证函数来创建自定义验证器,这个功能允许开发者实现特定于应用程序需求的验证逻辑;
- 验证链:Go Validator支持将多个验证器链接在一起,用于处理更复杂的验证场景,开发者可以创建一个验证器链,按顺序执行验证器,并在验证失败时停止,确保数据满足所有指定的条件;
- 错误处理:Go Validator提供详细的错误信息,帮助开发者轻松地找到验证失败的原因,开发者可以自定义这些错误信息,使其更适合特定的用例;
Go Validator 包根据 tags 对结构体和单个字段的值进行验证,它具备以下优秀的功能:
- 提供了一系列验证规则用于验证,并且支持自定义验证规则;
- 支持跨字段、跨结构体进行验证;
- 支持多维字段如 array、slice、map 等;
- 在验证接口类型前会先确定它的底层数据类型;
- 支持自定义字段类型比如 sql 驱动程序 Valuer;
- 可以自定义并支持国际化(i18n)的错误信息;
- 是 gin 框架的默认验证器。
1.2 Go Validator 功能介绍
1、范围比较验证
范围验证: 切片、数组 和 map、字符串,验证其长度;数值,验证大小范围
- lte:小于等于参数值,validate:“lte=3” (小于等于3)
- gte:大于等于参数值,validate:“lte=120,gte=0” (大于等于0小于等于120)
- lt:小于参数值,validate:“lt=3” (小于3)
- gt:大于参数值,validate:“lt=120,gt=0” (大于0小于120)
- len:等于参数值,validate:“len=2”,字符串长度必须为n,或者是数组、切片、map的len的值
- max:最大值,小于等于参数值,validate:“max=20” (小于等于20)
- min:最小值,大于等于参数值,validate:“min=2,max=20” (大于等于2小于等于20)
- ne:不等于,validate:“ne=2” (不等于2)
- oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,validate:“oneof=red green”
2、标记之间特殊符号说明
- 逗号( ,):把多个验证标记隔开。注意:隔开逗号之间不能有空格, validate:“lt=0,gt=100”,逗号那里不能有空格,否则panic
- 横线( - ):跳过该字段不验证
- 竖线( | ):使用多个验证标记,但是只需满足其中一个即可
- required:表示该字段值必输设置,且不能为默认值
- omitempty:如果字段未设置,则忽略它
3、字符串验证
- contains:包含参数子串,validate:“contains=tom” (字段的字符串值包含tom)
- excludes:包含参数子串,validate:“excludes=tom” (字段的字符串值不包含tom)
- startswith:以参数子串为前缀,validate:“startswith=golang”
- endswith:以参数子串为后缀,validate:“startswith=world”
4、特殊字符串验证
- email:验证字符串是email格式。默认为必填
- url:验证字符串是URL格式。默认为必填
- uri:字段值是否包含有效的uri,validate:“uri”
- ip:字段值是否包含有效的IP地址,validate:“ip”
- ipv4:字段值是否包含有效的ipv4地址,validate:“ipv4”
- ipv6:字段值是否包含有效的ipv6地址,validate:“ipv6”
二、 Go Validator 使用
2.1 Go Validator 包的安装、导入 及 初始化
要开始使用Go Validator,首先需要使用以下命令在 Go 项目中安装该库(如果使用 go mod,会自动下载):
1
|
go get github.com/go-playground/validator/v10
|
在源码中引入 validator 包 及 初始化 Validate:
1
2
3
4
|
import "github.com/go-playground/validator/v10"
var validate *validator.Validate
validate = validator.New()
|
validator.Validate是线程安全的,其变量内会缓存已经验证过结构体的特征,因此用户用一个变量更有利于提高效率。
2.2 使用 validate.Var方法 对普通变量进行验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
func main() {
var validate *validator.Validate
validate = validator.New()
var i int
// 只有i的值在区间(1,10)内才满足判断条件(gt=greater than, lt=less than)
errs := validate.Var(i, "gt=1,lt=10")
fmt.Println(errs)
myEmail := "joeybloggs.gmail.com"
errs = validate.Var(myEmail, "required,email") // 只有myEmail满足email地址格式才符合
fmt.Println(errs)
}
|
输出:
1
2
|
Key: '' Error:Field validation for '' failed on the 'gt' tag
Key: '' Error:Field validation for '' failed on the 'email' tag
|
2.3 使用 validate.Var方法 对 slice 进行验证
验证 slice 和 map 等复合数据类型时,通常需要加上 dive 表示递归验证,否则将不会深入验证数据类型内部元素。
示例1:一维切片要得验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
func main() {
var validate *validator.Validate
validate = validator.New()
sliceone := []string{"123", "onetwothree", "myslicetest", "four", "five"}
errs := validate.Var(sliceone, "max=15,dive,min=4")
fmt.Println(errs)
}
|
输出
1
2
|
Key: '[0]' Error:Field validation for '[0]' failed on the 'min' tag
Key: '' Error:Field validation for '' failed on the 'min' tag
|
validate.Var方法 的第二个参数中 tag关键字说明:
dive
前面的 max=15
,验证 [], 也就是验证slice的长度,此处限制了切片的最大长度为 15;
dive
后面的 min=4
,验证slice里的值长度,也就是说 dive 后面的 tag 验证 slice 的每个元素值,此处限制 slice 中每个元素值长度最小为 4;
示例2:二/多维切片要得验证
当然二维切片也差不多原理
1
2
3
|
slicethree := [][]string{}
err1 := validate.Var(slicethree, "min=2,dive,len=2,dive,required")
err2 := validate.Var(slicethree, "min=2,dive,dive,required")
|
err 输出:
1
2
|
Key: '' Error:Field validation for '' failed on the 'min' tag
Key: '' Error:Field validation for '' failed on the 'min' tag
|
说明,这里有2个 dive,刚好深入到二维slice,但它们也有不同之处,第二个表达式的第一个dive后没有设置tag:
- 第一个验证表达式:
- min=2:验证第一个 [] 方括号的值长度;
- len=2:验证第二个 []string 长度;
- required:验证slice里的值;
- 第二个验证表达式:
- min=2:验证第一个 [] 方括号的值长度;
- dive: 后没有设置tag值,不验证第二个 []string;
- required: 验证slice里的值;
2.4 对 map 进行验证
1、使用 validate.Var方法 对 map 进行验证
1
2
|
mapone := map[string]string{"one": "jimmmy", "two": "tom", "three": ""}
err := validate.Var(mapone, "gte=3,dive,keys,eq=1|eq=2,endkeys,required")
|
err 输出:
1
2
3
4
|
Key: '[three]' Error:Field validation for '[three]' failed on the 'eq=1|eq=3' tag
Key: '[three]' Error:Field validation for '[three]' failed on the 'required' tag
Key: '[one]' Error:Field validation for '[one]' failed on the 'eq=1|eq=3' tag
Key: '[two]' Error:Field validation for '[two]' failed on the 'eq=1|eq=3' tag
|
说明:
- gte=3:验证map的长度;
- dive后的 keys,eq=1|eq=2,endkeys:验证map的keys个数,也就是验证 [] 里值。上例中定义了一个string,所以明显报了3个错误。
- required:验证 map的值value
嵌套map核验:
如 map[[3]string]string
,和前面的 slice差不多,使用多个 dive。
示例:
1
2
|
var maptwo map[[3]string]string{}
err := validate.Var(maptwo, "gte=3,dive,keys,dive,eq=1|eq=3,endkeys,required")
|
说明:
- gte=3: 验证map的长度;
- keys,dive,eq=1|eq=3,endkeys:keys和endkeys中有一个dive(深入一级),验证map中key的数组每一个值
- required: 验证map的值
2、使用 validate.ValidateMap方法 对 map 进行验证
示例1:
1
2
3
|
user := map[string]interface{}{"name": "Arshiya Kiani", "email": "zytel3301@gmail.com"}
rules := map[string]interface{}{"name": "required,min=8,max=32", "email": "omitempty,required,email"}
errs := validate.ValidateMap(user, rules)
|
和数组一样,对于string类型,min和max关键字表示的是长度值;omitempty关键字表示如果为空则不进行验证。
示例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
data := map[string]interface{}{
"name": "Arshiya Kiani",
"email": "zytel3301@gmail.com",
"details": map[string]interface{}{
"family_members": map[string]interface{}{
"father_name": "Micheal",
"mother_name": "Hannah",
},
"salary": "1000",
},
}
rules := map[string]interface{}{
"name": "min=4,max=32",
"email": "required,email",
"details": map[string]interface{}{
"family_members": map[string]interface{}{
"father_name": "required,min=4,max=32",
"mother_name": "required,min=4,max=32",
},
"salary": "number",
},
}
if errs := validate.ValidateMap(data, rules); len(errs) != 0 {
fmt.Println(errs)
}
|
2.5 使用 validate.Struct方法 对结构体进行验证
在对结构体变量进行验证时,Validator 应用了 Golang 的 Struct Tag 和 Reflect机制,基本思想是:在 Struct Tag 中为不同的字段定义各自类型的约束,然后通过 Reflect 获取这些约束的类型信息并在校验器中进行数据校验。
结构体 Tag 相关验证的定义方式示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
type Address struct {
Street string `validate:"required"` // required关键字表示该字段不能为默认值,否则报错
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
// gte 表示 greater than or equal to;lte 表示 less than or equal to
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
// Iscolor alias for 'hexcolor|rgb|rgba|hsl|hsla'
FavouriteColor string `validate:"iscolor"`
// 对于数组Addresses, required表示其值不能为nil,但可以是{}。
// 如果需要对于数组的个数进行限制,同样可以使用lt,gt等数值比较的关键字。
// dive关键字表示对数组里的元素进行验证,如果数组内的元素也是一个struct,需要在该struct中写明验证规则。
// 通过dive关键字可以实现任意层级的验证逻辑。
Addresses []*Address `validate:"required,dive,required"`
}
type Test struct {
// 第一个required作用在[]string,表示它不能为nil
// gt=0表示元素的个数大于零
// dive表示下沉验证每个string元素
// 第二个required表示单个string元素不能为空
Array []string `validate:"required,gt=0,dive,required"`
// 第一个required表示整个map不能为nil,
// gt=0表示map元素的个数大于0,
// dive下沉到每对key,val pair里,
// keys和endkeys是对pair中的key进行验证,
// 使用validate.RegisterAlias函数对于验证公式keymax起了一个别名,
// 它等价于max=10,表示key的长度不能超过10byte,
// 第二个required后面的max=1000表示pair中val的最大长度为1000byte;
Map map[string]string `validate:"required,gt=0,dive,keys,keymax,endkeys,required,max=1000"`
}
validate.RegisterAlias("keymax", "max=10")
|
1、结构体验证基本使用
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
}
var validate *validator.Validate
func main() {
validate = validator.New()
validateStruct()
}
func validateStruct() {
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
}
err := validate.Struct(user)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Namespace()) // User.Age
fmt.Println(err.Field()) // Age
fmt.Println(err.StructNamespace()) // User.Age
fmt.Println(err.StructField()) // Age
fmt.Println(err.Tag()) // lte
fmt.Println(err.ActualTag()) // lte
fmt.Println(err.Kind()) // uint8
fmt.Println(err.Type()) // uint8
fmt.Println(err.Value()) // 135
fmt.Println(err.Param()) // 130
fmt.Println(err.Error()) // Key: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tag
fmt.Println()
}
}
}
|
2、结构体跨字段验证
跨字段的验证规则有很多,具体可以查看文末规则表。下面的示例使用了 eqfield 规则,它表示当前字段必须等于指定的字段。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Password string `validate:"required"`
// eqfield表示当前字段必须等于指定的 Password字段
ConfirmPassword string `validate:"required,eqfield=Password"`
}
var validate *validator.Validate
func main() {
validate = validator.New()
validateStruct()
}
func validateStruct() {
user := User{
Password: "password",
ConfirmPassword: "pass",
}
err := validate.Struct(user)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Error()) // Key: 'User.ConfirmPassword' Error:Field validation for 'ConfirmPassword' failed on the 'eqfield' tag
}
}
}
|
3、跨结构体验证
除了在结构体内跨字段验证,validator 还支持跨结构体验证,在规则表中可以发现,跨结构体的验证规则通常是在跨字段的验证规则的 field 前加上 cs,可以理解为 cross-struct。所以下面示例的 eacsfield 就是用来验证当前字段是否等于指定结构体的字段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
// eacsfield 是用来验证当前字段是否等于指定结构体Account的PayUid字段
Uid string `validate:"required,eqcsfield=Account.PayUid"`
Account Account
}
type Account struct {
PayUid string `validate:"required"`
}
var validate *validator.Validate
func main() {
validate = validator.New()
validateStruct()
}
func validateStruct() {
account := Account{
PayUid: "uid-1025",
}
user := User{
Uid: "uid-1024",
Account: account,
}
err := validate.Struct(user)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Error()) // Key: 'User.Uid' Error:Field validation for 'Uid' failed on the 'eqcsfield' tag
}
}
}
|
三、自定义类型验证器
3.1 自定义类型验证器
这种情况,通常不是对于单个字段进行验证,而是对于整个类型进行验证(复合条件)
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package main
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"github.com/go-playground/validator/v10"
)
type DbBackedUser struct {
Name sql.NullString `validate:"required"`
Age sql.NullInt64 `validate:"required"`
}
var validate *validator.Validate
func main() {
validate = validator.New()
// 注册所有的 sql.Null* 类型,使用 ValidateValuer 自定义类型函数进行验证
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
x := DbBackedUser{
Name: sql.NullString{String: "", Valid: true},
Age: sql.NullInt64{Int64: 0, Valid: false},
}
err := validate.Struct(x)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
}
// ValidateValuer 实现了 validator.CustomTypeFunc 接口
func ValidateValuer(field reflect.Value) interface{} {
// 如果字段的类型实现了 driver.Valuer 接口(即支持向数据库写入值),则进行验证
if valuer, ok := field.Interface().(driver.Valuer); ok {
val, err := valuer.Value() // 获取字段的值
if err == nil {
return val
}
// 处理错误
}
return nil
}
|
使用validate.RegisterCustomTypeFunc对于自定义类型进行注册,其中第一个参数为验证函数,后面的参数为自定义类型。
最终输出:
1
2
3
|
Err(s):
Key: 'DbBackedUser.Name' Error:Field validation for 'Name' failed on the 'required' tag
Key: 'DbBackedUser.Age' Error:Field validation for 'Age' failed on the 'required' tag
|
四、国际化/翻译
4.1 验证信息的国际化/翻译
在前面的示例中,可以看到返回的信息都是英文的,如果需要翻译成中文信息,首先需要安装翻译包:
1
2
|
go get -u github.com/go-playground/locales
go get -u github.com/go-playground/universal-translator
|
接着修改代码,实现翻译:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zhTrans "github.com/go-playground/validator/v10/translations/zh"
)
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
}
var validate *validator.Validate
var trans ut.Translator
func main() {
validate = validator.New()
// 中文翻译器
uniTrans := ut.New(zh.New())
trans, _ = uniTrans.GetTranslator("zh")
// 注册翻译器到验证器
err := zhTrans.RegisterDefaultTranslations(validate, trans)
if err != nil {
panic(fmt.Sprintf("registerDefaultTranslations fail: %s\n", err.Error()))
}
validateStruct()
}
func validateStruct() {
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
}
err := validate.Struct(user)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
// 翻译
fmt.Println(err.Translate(trans)) // Age必须小于或等于130
}
}
}
|
五、验证的错误处理
5.1 验证的错误处理
通过源码,可以发现 validator 返回的错误类型有两种,分别是参数错误 InvalidValidationError 和验证错误 ValidationErrors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
// $GOPATH/pkg/mod/github.com/go-playground/validator/v10/errors.go
// InvalidValidationError describes an invalid argument passed to
// `Struct`, `StructExcept`, StructPartial` or `Field`
type InvalidValidationError struct {
Type reflect.Type
}
// Error returns InvalidValidationError message
func (e *InvalidValidationError) Error() string {
if e.Type == nil {
return "validator: (nil)"
}
return "validator: (nil " + e.Type.String() + ")"
}
// ValidationErrors is an array of FieldError's
// for use in custom error messages post validation.
type ValidationErrors []FieldError
// Error is intended for use in development + debugging and not intended to be a production error message.
// It allows ValidationErrors to subscribe to the Error interface.
// All information to create an error message specific to your application is contained within
// the FieldError found within the ValidationErrors array
func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("")
for i := 0; i < len(ve); i++ {
buff.WriteString(ve[i].Error())
buff.WriteString("\n")
}
return strings.TrimSpace(buff.String())
}
|
从源码可以看到 validator 返回的错误有两种,一种是参数错误,一种是校验错误,它们都实现了 error 接口:
- 参数错误时,返回 InvalidValidationError 类型;
- 校验错误时,返回 ValidationErrors 类型。ValidationErrors 是一个错误切片,保存了每个字段违反的每个约束信息。
所以 validator 校验返回的结果有 3 种情况:
- nil:没有错误;
- InvalidValidationError:输入参数错误;
- ValidationErrors:字段违反约束。
可以在程序中判断 err != nil 时,可以依次将 err转换为 InvalidValidationError 和 ValidationErrors 以获取更详细的信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
err := validate.Struct(user)
if err != nil {
invalid, ok := err.(*validator.InvalidValidationError)
if ok {
fmt.Println("param error:", invalid)
return
}
validationErrs := err.(validator.ValidationErrors)
for _, validationErr := range validationErrs {
fmt.Println(validationErr)
}
}
|
六、Go Validator 常用验证规则
6.1 经常用到的验证规则
tag |
说明 |
示例 |
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” |
min |
最小值 |
Field validate:“min=1” |
max |
最大值 |
Field validate:“max=2” |
required_with其他字段其中一个不为空且当前字段不为空 |
Field validate:“required_with=Field1 Field2” |
|
required_without |
其他字段其中一个为空且当前字段不为空 |
Field `validate:“required_without=Field1 Field2” |
lowercase |
符串值是否只包含小写字符 |
Field validate:“lowercase” |
uppercase |
符串值是否只包含大写字符 |
Field validate:“uppercase” |
email |
字符串值包含一个有效的电子邮件 |
Field validate:“email” |
json |
字符串值是否为有效的JSON |
Field validate:“json” |
url |
符串值是否包含有效的url |
Field validate:“url” |
uri |
符串值是否包含有效的 uri |
Field validate:“uri” |
contains |
字符串值包含子字符串值 |
Field validate:“contains=@” |
excludes |
字符串值不包含子字符串值 |
字符串值不包含子字符串值 Field validate:“excludes=@” |
ip |
字符串值是否包含有效的 IP 地址 |
Field validate:“ip” |
datetime |
字符串值是否包含有效的日期 |
Field validate:“datetime” |
startswith |
字符串以提供的字符串值开始 |
Field validate:“startswith=abc” |
endswith |
字符串以提供的字符串值结束 |
Field validate:“endswith=abc” |
required相关字段
关键字 |
含义 |
举例 |
Required |
该字段不能为默认值 |
数值类型不能为零,string类型不能为空字符串,slices, maps, pointers, interfaces, channels, functions这些类型不能为nil |
Required_if |
当依赖字段为特定值时,该字段不能为默认值 |
// require the field if the Field1 is equal to the parameter given,Usage: required_if=Field1 foobar // require the field if the Field1 and Field2 is equal to the value respectively,Usage: required_if=Field1 foo Field2 bar |
Required_unless |
与Required_if含义相反 |
// require the field unless the Field1 is equal to the parameter given, Usage: required_unless=Field1 foobar // require the field unless the Field1 and Field2 is equal to the value respectively, Usage: required_unless=Field1 foo Field2 bar |
required_with |
当依赖字段出现时,该字段不能为默认值 |
// require the field if the Field1 is present, Usage: required_with=Field1 // require the field if the Field1 or Field2 is present, Usage: required_with=Field1 Field2 |
required_with_all |
当依赖字段全部出现时,该字段不能为默认值 |
// require the field if the Field1 and Field2 is present, Usage: required_with_all=Field1 Field2 |
required_without |
required_with相反 |
|
required_without_all |
与required_with_all相反 |
|
6.2 常用验证规则分类
1、比较
规则 |
描述 |
eq |
等于 |
eq_ignore_case |
等于,忽略大小写 |
ne |
不等于 |
ne_ignore_case |
不等于,忽略大小写 |
gt |
大于 |
gte |
大于等于 |
lt |
小于 |
lte |
小于等于 |
2、跨字段校验
validator 允许定义跨字段验证,即:验证某个字段与其他字段之间的关系。这种验证实际上分为两种:
- 一种是参数字段就是同一个结构体中的平级字段。
- 另一种是参数字段为结构中其他字段的字段。
验证语法很简单,如果是验证同一个结构中的字段,则在基础的 Tags 后面添加一个 field 后缀,例如:eqfield 定义字段间的相等(eq)约束。如果是更深层次的字段,在 field 之前还需要加上 cs(Cross-Struct),eq 就变为了 eqcsfield。
- eqfield=Field:必须等于 Field 的值;
- nefield=Field:必须不等于 Field 的值;
- gtfield=Field:必须大于 Field 的值;
- gtefield=Field: 必须大于等于 Field 的值;
- ltfield=Field:必须小于 Field 的值;
- ltefield=Field:必须小于等于 Field 的值;
- eqcsfield=Other.Field:必须等于 struct Other 中 Field 的值;
- necsfield=Other.Field:必须不等于 struct Other 中 Field 的值;
- gtcsfield=Other.Field:必须大于 struct Other 中 Field 的值;
- gtecsfield=Other.Field:必须大于等于 struct Other 中 Field 的值;
- ltcsfield=Other.Field:必须小于 struct Other 中 Field 的值;
- ltecsfield=Other.Field:必须小于等于 struct Other 中 Field 的值;
另外还有几个常用的 Tag:
- required_with=Field1 Field2:在 Field1 或者 Field2 存在时,必须;
- required_with_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须;
- required_without=Field1 Field2:在 Field1 或者 Field2 不存在时,必须;
- required_without_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须;
Tag |
Description |
eqfield |
当前字段必须等于指定字段 |
nefield |
当前字段必须不等于指定字段 |
gtfield |
当前字段必须大于指定字段 |
gtefield |
当前字段必须大于等于指定字段 |
ltfield |
当前字段必须小于指定字段 |
ltefield |
当前字段必须小于等于指定字段 |
fieldcontains |
当前字段必须包含指定字段,只能用于字符串类型 |
fieldexcludes |
当前字段必须不包含指定字段,只能用于字符串类型 |
3、跨结构体跨字段校验
跨结构体跨字段校验 规则与 跨字段校验 规则类似,一般是在 跨字段校验 规则的 field 前加上 cs 字符。
Tag |
Description |
eqcsfield |
当前字段必须等于指定结构体的字段 |
necsfield |
当前字段必须不等于指定结构体的字段 |
gtcsfield |
当前字段必须大于指定结构体的字段 |
gtecsfield |
当前字段必须大于等于指定结构体的字段 |
ltcsfield |
当前字段必须小于指定结构体的字段 |
ltecsfield |
当前字段必须小于等于指定结构体的字段 |
4、字符串相关验证
Tag |
Description |
alpha |
仅限字母 |
alphanum |
仅限字母、数字 |
alphanumunicode |
仅限字母、数字和 Unicode |
alphaunicode |
字母和 Unicode |
ascii |
ASCII |
boolean |
当前字段必须是能够被 strconv.ParseBool 解析为字符串的值 |
contains |
当前字段必须包含指定字符串 |
startswith |
当前字段必须以指定字符串开头 |
startsnotwith |
当前字段必须不是以指定字符串开头 |
endswith |
当前字段必须以指定字符串结尾 |
endsnotwith |
当前字段必须不是以指定字符串结尾 |
uppercase |
当前字段的字母必须是大写,可包含数字,不能为空 |
lowercase |
当前字段的字母必须是小写,可包含数字,不能为空 |
5、格式化验证
Tag |
Description |
base64 |
当前字段必须是有效的 Base64 值,不能为空 |
base64url |
当前字段必须是包含根据 RFC4648 规范的有效 base64 URL 安全值。 |
json |
正确的 JSON 串 |
rgb |
正确的 RGB 字符串 |
rgba |
正确的 RGBA 字符串 |
6、其它验证
Tag |
Description |
dir |
指定字段的值必须是已存在的目录 |
dirpath |
指定字段的值必须是合法的目录路径 |
file |
指定字段的值必须是已存在的文件 |
filepath |
指定字段的值必须是合法的文件路径 |
len |
当前字段的长度必须指定值,可用于 string、slice 等 |
max |
当前字段的最大值必须是指定值 |
min |
当前字段的最小值必须是指定值 |
required |
当前字段为必填项,且不能为零值 |
required_if |
当指定字段等于给定值时,当前字段使用 required 验证。如 required_if=Field1 foo Field2 bar |
required_unless |
当指定字段不等于给定值时,当前字段使用 required 验证。如 required_unless=Field1 foo Field2 bar |
required_with |
当任一指定字段不为零值时,当前字段使用 required 验证。如 required_with=Field1 Field2 |
required_with_all |
当所有指定字段不为零值时,当前字段使用 required 验证。 |