Skip to content

validator

validator 是一个数据验证的包

通常我们使用它处理前端发来的字段验证

一个简单的验证

加上validate:"required" 这个字段变成必填字段

go
package main

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

// 申明一个validator对象
var validate *validator.Validate

type User struct {
	// 必选
	Name string `validate:"required,min=2,max=10"`
	Age uint `validate:"required,min=2,max=10"`
}
func main() {

	var user = User{}

	validate = validator.New()
	// 验证结构是否符合规则
	err := validate.Struct(user)
	fmt.Printf("err: %v\n", err)
	// 当结构体出错时 err 对象就会存在值
	if err != nil {
		for _, err := range err.(validator.ValidationErrors) {
			// 输出错误信息
			// fmt.Println("Namespace >>>", err.Namespace())
			// fmt.Println("StructNamespace >>>", err.StructNamespace())
			// fmt.Println("StructField >>>", err.StructField())
			// fmt.Println("Tag >>>", err.Tag())
			// fmt.Println("ActualTag >>>", err.ActualTag())
			// fmt.Println("Kind >>>", err.Kind())、
			fmt.Println("Field >>>", err.Field())
			fmt.Println("Type >>>", err.Type())
			fmt.Println("Value >>>", err.Value())
			// fmt.Println("Param >>>", err.Param())
		}
	}
}
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

当验证失败的时候就会输出错误内容

约束数值长度

go
package main

import (
	"fmt"

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

// 申明一个validator对象
var validate *validator.Validate

type User struct {
	// 必选 长度必须 >=2 <= 10
	Name string `validate:"required,min=2,max=10"`
	// 必选 必须>0 < 130
	Age  uint   `validate:"gte=0,lte=130"`
}

func main() {

	var user = User{
		Name: "t",
		Age: 150,
	}

	validate = validator.New()
	// 验证结构是否符合规则
	err := validate.Struct(user)
	fmt.Printf("err: %v\n", err)
	// 当结构体出错时 err 对象就会存在值
	if err != nil {
		for _, err := range err.(validator.ValidationErrors) {
			fmt.Println("Field >>>", err.Field())
			fmt.Println("Type >>>", err.Type())
			fmt.Println("Value >>>", err.Value())
		}
	}
}
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

抛出错误

我们上面已经看到了使用minmax来约束字符串的长度或数值的范围,范围约束的字段类型有以下几种:

  • 对于数值,则约束其值;
  • 对于字符串,则约束其长度;
  • 对于切片、数组和map,则约束其长度。

其他的内置约束范围字段

tag作用
len限制长度len=10 , 对于不同类型参考max``min
eq限制内容相等eq=666
ne不等于参考值ne=666
gt大于参数值,例如gt=10
gte大于等于参数值,例如gte=10
lt小于参数值,例如lt=10
lte小于等于参数值,例如lte=10
oneof只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,例如oneof=red green

跨字段约束

validator允许定义跨字段的约束,即该字段与其他字段之间的关系。

增加"eqfield=Password"的验证,限制第二个密码必须要等于第一个密码

go
type User struct {
	// 必选 长度必须 >=2 <= 10
	Name string `validate:"len=5"`
	// 必选 必须>0 < 130
	Age  uint   `validate:"len=10"`
	// 密码必须至少是10位
	Password  string `validate:"min=10"`
	// 第二个密码必须要等于第一个密码
    Password2 string `validate:"eqfield=Password"`
}
1
2
3
4
5
6
7
8
9
10

对于字符串的限制

  • contains=xxx 必须包含某个字符串
go
type User struct {
	Name string `validate:"contains=我是"`
}
1
2
3
  • excludes=😀 不包含参数字符串
go
type User struct {
	Name string `validate:"excludes=😀"`
}
1
2
3
  • startswith=xxx 以参数字符串开头
  • endswith=xxx 以参数字符串结尾

唯一性

使用unqiue来规定唯一性

  • 对于数组和切片,unique约束没有重复的元素;
  • 对于map,unique约束没有重复的值;
  • 对于元素类型为结构体的切片,unique约束结构体对象的某个字段不重复,通过unqiue=field指定这个字段名。
go
type User struct {
	Name string `validate:"min=2"`
	// 限制不允许有相同名字的朋友
	Friends []User `validate:"unique=Name"`
	// 限制不能有相同字符串的爱好
	Likes []string `validate:"unique"`
}

func main() {

	var user1 = User{
		Name: "张三",
	}
	var user2 = User{
		Name: "张三",
	}

	var user3 = User{
		Name:    "王五",
		Friends: []User{user1, user2},
		Likes:   []string{"打篮球", "rap", "rap",""},
	}

	validate = validator.New()
	err = validate.Struct(user3)
	if err != nil {
		fmt.Printf("err3: %v\n", err)
	}
}
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

其他

validator 提供了大量内置数据格式验证,如邮箱 \ 颜色 等等,具体可以查看文档

自定义验证规则

go
var validate *validator.Validate
type User struct {
	Name  string `validate:"min=2"`
	Phone int    `validate:"checkPhone"`
}

func main() {

	var user1 = User{
		Name:  "张三",
		Phone: 185222200,
	}

	validate = validator.New()

	// 注册一个自己的验证方式
	_ = validate.RegisterValidation("checkPhone", func(fl validator.FieldLevel) bool {
		val := fl.Field().Int()
		s := strconv.FormatInt(val, 10)
		reg := regexp.MustCompile("^1(3\\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$")
		b := reg.MatchString(s)
		return b
	})

	err := validate.Struct(user1)
	if err != nil {
		fmt.Printf("err1: %v\n", err)
	}
}
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

错误处理

validate会返回一个错误对象

go
err := validate.Struct(user1)
1

err 存在三种情况

  1. nil = 没有错误
  2. InvalidValidationError = 输入参数有无
  3. ValidationErrors = 字段违反了约束 我们可以在程序中判断err != nil时,依次将err转换为InvalidValidationErrorValidationErrors以获取更详细的信息:
go
func processErr(err error) {
	if err == nil {
		return
	}
	validationError, ok := err.(*validator.InvalidValidationError)
	if ok {
		fmt.Printf("param error:%v", validationError)
		return
	}

	validationErrs := err.(validator.ValidationErrors)
	for _, validationErr := range validationErrs {
		fmt.Printf("validationErr:%v \n", validationErr)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

错误信息基本上是这样的

Key: 'User.Phone' Error:Field validation for 'Phone' failed on the 'checkPhone' tag
1

假如我们希望给得到更加友好的提示,就需要设置一个映射来处理错误了

我们可以将这个validator封装成一个模块

go
package validate

import (
	"github.com/go-playground/validator/v10"
	"regexp"
	"strconv"
)

var Validate *validator.Validate

// MessageMap 使用tag作为key来映射一些中文提示
var MessageMap map[string]string = map[string]string{
	"checkPhone": "手机格式不正确",
	"min":        "长度不够",
}

// InitValidate 初始化Validate
func InitValidate() {
	Validate = validator.New()
	_ = Validate.RegisterValidation("checkPhone", checkPhone)
}

// ProcessErr 处理错误信息
func ProcessErr(err error) (errMessage string) {
	if err == nil {
		return ""
	}

	validationErrs := err.(validator.ValidationErrors)

	var message = ""
	for _, validationErr := range validationErrs {
		tag := validationErr.Tag()
		namespace := validationErr.Namespace()

		// 假如存在映射时,将其替换成映射的值
		if MessageMap[tag] != "" {
			tag = MessageMap[tag]
		}
		message += namespace + tag + "\n"
	}

	return message
}

// 确认手机号
func checkPhone(fl validator.FieldLevel) bool {
	val := fl.Field().Int()
	s := strconv.FormatInt(val, 10)
	reg := regexp.MustCompile("^1(3\\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$")
	b := reg.MatchString(s)
	return b
}

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
52
53
54

main.go中使用

go
package main

import "code1/validate"

func init() {
	// 初始化验证实例
	validate.InitValidate()
}

type User struct {
	Name  string `validate:"min=2"`
	Phone int    `validate:"checkPhone"`
}

func main() {
	user := User{
		Name:  "",
		Phone: 1775059664701,
	}

	err := validate.Instance.Struct(user)
	errMessage := validate.ProcessErr(err)
	println(errMessage)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

运行后我们就得到了一些相对友好的提示

User.Name长度不够
User.Phone手机格式不正确
1
2

参考文章