Golang 18_Golang验证器包Validator

一、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 验证。