validator实现参数校验

一.导入包:

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

二.基本语法:

标记 标记说明
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"
eqfield 同一结构体字段相等 Field validate:"eqfield=Field2"
nefield 同一结构体字段不相等 Field validate:"nefield=Field2"
gtfield 大于同一结构体字段 Field validate:"gtfield=Field2"
gtefield 大于等于同一结构体字段 Field validate:"gtefield=Field2"
ltfield 小于同一结构体字段 Field validate:"ltfield=Field2"
ltefield 小于等于同一结构体字段 Field validate:"ltefield=Field2"
eqcsfield 跨不同结构体字段相等 Struct1.Field validate:"eqcsfield=Struct2.Field2"
min 最小值 Field validate:"min=1"
max 最大值 Field validate:"max=2"
isdefault 是默认值 Field validate:"isdefault=0"
oneof 其中之一 Field validate:"oneof=5 7 9"
containsfield 字段包含另一个字段 Field validate:"containsfield=Field2"
email 字符串值包含一个有效的电子邮件 Field validate:"email"
json 字符串值是否为有效的 JSON Field validate:"json"
file 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 Field validate:"file"
url 符串值是否包含有效的 url Field validate:"url"
ip 字符串值是否包含有效的 IP 地址 Field validate:"ip"
ipv4 字符串值是否包含有效的 ipv4地址 Field validate:"ipv4"
datetime 字符串值是否包含有效的 日期 Field validate:"datetime"

三.使用注意

  1. 当搜索条件与特殊标记冲突时,如:逗号(,),或操作(|),中横线(-)等则需要使用 UTF-8十六进制表示形式

如:

1
2
3
4
type Test struct {
Field1 string `validate:"excludesall=|"` // 错误
Field2 string `validate:"excludesall=0x7C"` // 正确.
}
  1. 可通过validationErrors := errs.(validator.ValidationErrors)获取错误对象自定义返回响应错误

  2. 自定义校验结果翻译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 初始化翻译器
func validateInit() {
zh_ch := zh.New()
uni := ut.New(zh_ch) // 万能翻译器,保存所有的语言环境和翻译数据
Trans, _ = uni.GetTranslator("zh") // 翻译器
Validate = validator.New()
_ = zh_translations.RegisterDefaultTranslations(Validate, Trans)
// 添加额外翻译
_ = Validate.RegisterTranslation("required_without", Trans, func(ut ut.Translator) error {
return ut.Add("required_without", "{0} 为必填字段!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required_without", fe.Field())
return t
})
}
  1. 如果需要校验多种,需要使用,进行分隔,如下:
1
2
3
4
5
type UserLoginRequest struct {
CaptchaCode string `json:"captcha_code" validate:"required,ValidateCaptchaCodeLength"`
UserName string `json:"user_name" validate:"required,min=5,max=16" label:"用户名"`
Password string `json:"password" validate:"required,min=5,max=16" label:"密码"`
}

四.使用案例:

4.1 实现国际化/中文:

translator := ut.New(zh.New())可以实现中文翻译

trans, _ = translator.GetTranslator("zh")这里有个坑,就是第二个参数是bool,不管是en还是zh都是会报错的,忽略可以正常运行。

RegisterTagNameFunc就是获取我们自定义的字段的tag,后面就可以实现这个label的值加上报错信息了(label的值为中文)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func InitValidator() (*validator.Validate, ut.Translator) {
validate = validator.New()
translator := ut.New(zh.New())
trans, _ = translator.GetTranslator("zh")
err := zhTrans.RegisterDefaultTranslations(validate, trans)
// 注册自定义方法 可以实现 label字段+错误信息
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("label")
return name
})
if err != nil {
panic(fmt.Sprintf("registerDefaultTranslations fail: %s\n", err.Error()))
}
return validate, trans
}

在gin中如何校验?

使用validator.Struct就可以对其进行校验了,如下:

1
2
3
4
5
6
if err := global.Validator.Struct(&req); err != nil {
for _, fieldError := range err.(validator.ValidationErrors) {
response.FailWithMessage(fieldError.Translate(global.Translator), ctx)
return
}
}

这里的fieldError我们可以获取当前报错的字段的信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()
}

4.2 自定义校验方法:

需要自定义校验方法,只需要使用RegisterValidation即可。

1
2
3
4
5
6
7
8
// RegisterValidation adds a validation with the given tag
//
// NOTES:
// - if the key already exists, the previous validation function will be replaced.
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
}

Func是一个type Func func(fl FieldLevel) bool

这里的方法,可以写指针接收者,也可以写直接收者都是无所谓的,返回值一定需要是布尔类型,

完整代码如下:

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

import (
"fmt"

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

type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"validateAge"`
}

func (u *User) ValidateAge(fl validator.FieldLevel) bool {
// 在自定义验证方法中获取字段的值
return fl.Field().Uint() > 4
}

var validate *validator.Validate

func main() {
validate = validator.New()
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 34,
}
validate.RegisterValidation("validateAge", user.ValidateAge)
validateStruct(user)
}

func validateStruct(user *User) {

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()
}
}
}

关键点在于:

  1. tag中定义我们需要的校验方法
  2. 使用RegisterValidation(tag,function) bool方法注册校验方法

validator实现参数校验
https://pow1e.github.io/2023/12/02/golang/validator实现参数校验/
作者
pow1e
发布于
2023年12月2日
许可协议