Golang常用包 - validator
hanpy

validator 是一个开源的验证器包,可以快速校验输入信息是否符合自定规则。

介绍

validator 可以验证单个变量、Map、Slice、还有结构体,有丰富的验证规则,而且还能自定义规则,在web开发中有广泛的应用。

项目地址

https://github.com/go-playground/validator

安装

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

验证单个变量

1
2
3
4
5
email := "hanpy126.com"
err := validate.Var(email, "email")
if err != nil {
fmt.Println(err) // output: Key: "" Error:Field validation for "" failed on the "email" 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
var validate *validator.Validate = validator.New()

type User struct {
Name string `validate:"required"`
Age int `validate:"required"`
}

type Addr struct {
City string
Street string
}

func TestValidateStruct1(t *testing.T) {
user := User{}
err := validate.Struct(user)
if err != nil {
// Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag
// Key: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag
fmt.Println(err)
}
}

func TestValidateStruct2(t *testing.T) {
// 这种情况适用于在结构体中没有定义tag和验证规则
rules := map[string]string{
"City": "required",
"Street": "required",
}
// 注册一个验证规则,如果结构体上面有验证规则,这个方法会覆盖
validate.RegisterStructValidationMapRules(rules, Addr{})

addr := Addr{City: "北京"}
err := validate.Struct(addr)
if err != nil {
// Key: 'Addr.Street' Error:Field validation for 'Street' failed on the 'required' tag
fmt.Println(err)
}
}

验证slice

需要用到dive,max 验证slice长度,min验证没一个元素的长度

1
2
3
4
5
6
sliceone := []string{"123", "onetwothree", "myslicetest", "four", "five"}
err = validate.Var(sliceone, "max=15,dive,min=4")
if err != nil {
// Key: '[0]' Error:Field validation for '[0]' failed on the 'min' tag
fmt.Println(err)
}

验证Map

map的验证中也需要tag关键字 dive, 另外,它还有 keys 和 endkeys 两tag,验证这2个tag之间map的 key,而不是value值。

1
2
3
4
5
6
7
8
9
10
var mapone map[string]string
mapone = map[string]string{"one": "jimmmy", "two": "tom", "three": ""}
err = validate.Var(mapone, "gte=3,dive,keys,eq=1|eq=2,endkeys,required")
if err != nil {
// Key: '[one]' Error:Field validation for '[one]' failed on the 'eq=1|eq=2' tag
// Key: '[two]' Error:Field validation for '[two]' failed on the 'eq=1|eq=2' tag
// Key: '[three]' Error:Field validation for '[three]' failed on the 'eq=1|eq=2' tag
// Key: '[three]' Error:Field validation for '[three]' failed on the 'required' tag
fmt.Println(err)
}

常用的验证规则

字段验证

https://github.com/go-playground/validator/blob/master/README.md#fields

带有cs的都是跨结构体验证

标记 标记说明 举例
eqfield 同一结构体字段验证相等
nefield 同一结构体字段验证不相等 validate:”eqfield=Password”
eqcsfield 跨不同结构体字段验证
necsfield 跨不同结构体字段不相等
gtefield 大于等于同一结构体字段
ltefield 小于等于同一结构体字段

网络验证

https://github.com/go-playground/validator/blob/master/README.md#network

标记 标记说明 举例
ip 字段值是否包含有效的IP地址 validate:"ip"

字符串验证

标记 标记说明 举例
ip 字段值是否包含有效的IP地址 validate:"ip"

自定义验证规则

修改获取验证规则的标签名字

SetTagName 修改验证默认的标签,默认的标签是validate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 默认的标签是validate
type User struct {
Name string `validate:"required"`
Age int `validate:"required"`
}

// 修改为check
validate.SetTagName("check")

// 就可以这么写了
type User struct {
Name string `check:"required"`
Age int `check:"required"`
}

注册一个获取tag的自定义方法

RegisterTagNameFunc 默认情况下在错误信息中是获取的结构体的字段,可以修改为获取字段描述中其他字段的,比如说 json 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
var validate *validator.Validate = validator.New()

type User struct {
Name string `json:"name_json" form:"name_form" validate:"required"`
Age int `json:"age_json" form:"age_form" validate:"required"`
}

func TestValidateStruct1(t *testing.T) {
user := User{}
err := validate.Struct(user)
if err != nil {
// Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag
// Key: 'User.Age' Error:Field validation for 'Age' failed on the 'required' tag
fmt.Println(err)
}
}

--------------------------------------------------------------------------

// 注册一个获取json tag的自定义方法
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// skip if tag key says it should be ignored
if name == "-" {
return ""
}
return name
})

func TestValidateStruct1(t *testing.T) {
user := User{}
err := validate.Struct(user)
if err != nil {
// Key: 'User.name_json' Error:Field validation for 'name_json' failed on the 'required' tag
// Key: 'User.age_json' Error:Field validation for 'age_json' failed on the 'required' tag
fmt.Println(err)
}
}

为一个自定义标签创建验证规则

RegisterValidation 函数接收一个标签名字和一个验证函数。

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
package test

import (
"fmt"
"github.com/go-playground/validator/v10"
"testing"
)

var validate *validator.Validate = validator.New()

type User struct {
Name string `validate:"required,check_name"`
Age int `validate:"required"`
}

//
// TestRegisterValidation
// @Description: 自定义tag创建验证规则
// @param t
//
func TestRegisterValidation(t *testing.T) {
// 为tag为check_name注册验证规则
_ = validate.RegisterValidation("check_name", CheckName)

user := User{"zhangsan", 30}
err := validate.Struct(user)
if err != nil {
// Key: 'User.Name' Error:Field validation for 'Name' failed on the 'check_name' tag
fmt.Println(err)
}

fmt.Println("Check Done!")
}

//
// CheckName
// @Description: 自定义验证规则-验证name
// @param f
//
func CheckName(f validator.FieldLevel) bool {
// 获取字段的值,然后判断是否等于"hanpy"
return f.Field().String() == "hanpy"
}

为结构体注册验证规则

RegisterStructValidation

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
package test

import (
"fmt"
"github.com/go-playground/validator/v10"
"testing"
)

var validate *validator.Validate = validator.New()

type User struct {
Name string `validate:"required"`
Age int `validate:"required"`
}

//
// TestRegisterStructValidation
// @Description: 为结构体注册验证规则
// @param t
//
func TestRegisterStructValidation(t *testing.T) {
// 为User注册规则
validate.RegisterStructValidation(UserStructLevelValidation, User{})

user := User{"zha", 30}
err := validate.Struct(user)
if err != nil {
// Key: 'User.fname' Error:Field validation for 'fname' failed on the 'fnameorlname' tag
fmt.Println(err)
}

fmt.Println("Check Done!")
}

//
// UserStructLevelValidation
// @Description: 自定义结构体验证
// @param sl
//
func UserStructLevelValidation(sl validator.StructLevel) {
user := sl.Current().Interface().(User)
if len(user.Name) == 3 {
sl.ReportError(user.Name, "fname", "FirstName", "fnameorlname", "")
}
}

为某个类型创建验证规则

RegisterCustomTypeFunc 可以为某种类创建验证规则

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
package test

import (
"fmt"
"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"
zhs "github.com/go-playground/validator/v10/translations/zh"
"testing"
)

var (
validate = validator.New() // 实例化验证器
chinese = zh.New() // 获取中文翻译器
english = en.New() // 英文翻译器
uni = ut.New(english, chinese) // 第一个参数是必填,如果没有其他的语言设置,就用这第一个; 设置成中文翻译器
trans, _ = uni.GetTranslator("zh") // 获取翻译字典
)

type Teacher struct {
Name string `validate:"required,min=3,max=5"`
Email string `validate:"email"`
Age int8 `validate:"gte=18,lte=20"`
}

func TestValidatorZh(t *testing.T) {
// 注册翻译器
_ = zhs.RegisterDefaultTranslations(validate, trans)

teacher := Teacher{"zh", "han126.com", 10}
err := validate.Struct(teacher)
if err != nil {
// map[Teacher.Age:Age必须大于或等于18 Teacher.Email:Email必须是一个有效的邮箱 Teacher.Name:Name长度必须至少为3个字符]
if errors, ok := err.(validator.ValidationErrors); ok {
fmt.Println(errors.Translate(trans))
}
}
}

为字段添加描述

Todo

可以为结构体中的字段添加一个描述,验证失败的时候可以进行中文的提示,比如下面这种

1
2
3
4
5
type User struct {
Name string `json:"name" validate:"required" desc:"姓名"`
}

// 最后要达到的效果是,验证失败提示:姓名必填