项目地址: 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" |
necsfield | 跨不同结构体字段不相等 | Struct1.Field validate:"necsfield=Struct2.Field2" |
gtcsfield | 大于跨不同结构体字段 | Struct1.Field validate:"gtcsfield=Struct2.Field2" |
gtecsfield | 大于等于跨不同结构体字段 | Struct1.Field validate:"gtecsfield=Struct2.Field2" |
ltcsfield | 小于跨不同结构体字段 | Struct1.Field validate:"ltcsfield=Struct2.Field2" |
ltecsfield | 小于等于跨不同结构体字段 | Struct1.Field validate:"ltecsfield=Struct2.Field2" |
min | 最大值 | Field validate:"min=1" |
max | 最小值 | Field validate:"max=2" |
structonly | 仅验证结构体,不验证任何结构体字段 | Struct validate:"structonly" |
nostructlevel | 不运行任何结构级别的验证 | Struct validate:"nostructlevel" |
dive | 向下延伸验证,多层向下需要多个dive标记 | [][]string validate:"gt=0,dive,len=1,dive,required" |
dive Keys & EndKeys | 与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值 | map[string]string validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,required" |
required_with | 其他字段其中一个不为空且当前字段不为空 | Field validate:"required_with=Field1 Field2" |
required_with_all | 其他所有字段不为空且当前字段不为空 | Field validate:"required_with_all=Field1 Field2" |
required_without | 其他字段其中一个为空且当前字段不为空 | Field `validate:"required_without=Field1 Field2" |
required_without_all | 其他所有字段为空且当前字段不为空 | Field validate:"required_without_all=Field1 Field2" |
isdefault | 是默认值 | Field validate:"isdefault=0" |
oneof | 其中之一 | Field validate:"oneof=5 7 9" |
containsfield | 字段包含另一个字段 | Field validate:"containsfield=Field2" |
excludesfield | 字段不包含另一个字段 | Field validate:"excludesfield=Field2" |
unique | 是否唯一,通常用于切片或结构体 | Field validate:"unique" |
alphanum | 字符串值是否只包含 ASCII 字母数字字符 | Field validate:"alphanum" |
alphaunicode | 字符串值是否只包含 unicode 字符 | Field validate:"alphaunicode" |
alphanumunicode | 字符串值是否只包含 unicode 字母数字字符 | Field validate:"alphanumunicode" |
numeric | 字符串值是否包含基本的数值 | Field validate:"numeric" |
hexadecimal | 字符串值是否包含有效的十六进制 | Field validate:"hexadecimal" |
hexcolor | 字符串值是否包含有效的十六进制颜色 | Field validate:"hexcolor" |
lowercase | 符串值是否只包含小写字符 | Field validate:"lowercase" |
uppercase | 符串值是否只包含大写字符 | Field validate:"uppercase" |
字符串值包含一个有效的电子邮件 | Field validate:"email" | |
json | 字符串值是否为有效的 JSON | Field validate:"json" |
file | 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 | Field validate:"file" |
url | 符串值是否包含有效的 url | Field validate:"url" |
uri | 符串值是否包含有效的 uri | Field validate:"uri" |
base64 | 字符串值是否包含有效的 base64值 | Field validate:"base64" |
contains | 字符串值包含子字符串值 | Field validate:"contains=@" |
containsany | 字符串值包含子字符串值中的任何字符 | Field validate:"containsany=abc" |
containsrune | 字符串值包含提供的特殊符号值 | Field validate:"containsrune=☢" |
excludes | 字符串值不包含子字符串值 | Field validate:"excludes=@" |
excludesall | 字符串值不包含任何子字符串值 | Field validate:"excludesall=abc" |
excludesrune | 字符串值不包含提供的特殊符号值 | Field validate:"containsrune=☢" |
startswith | 字符串以提供的字符串值开始 | Field validate:"startswith=abc" |
endswith | 字符串以提供的字符串值结束 | Field validate:"endswith=abc" |
ip | 字符串值是否包含有效的 IP 地址 | Field validate:"ip" |
ipv4 | 字符串值是否包含有效的 ipv4地址 | Field validate:"ipv4" |
datetime | 字符串值是否包含有效的 日期 | Field validate:"datetime" |
单字段校验
模拟对前端传参进行校验
单字段多个条件 校验
代码语言:javascript复制package main
import (
"fmt"
"strings"
"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"
zhtrans "github.com/go-playground/validator/v10/translations/zh"
// entrans "github.com/go-playground/validator/v10/translations/en"
)
/*
https://www.cnblogs.com/jiujuan/p/13823864.html
https://www.liwenzhou.com/posts/Go/validator-usages/
https://juejin.cn/post/7056823502640250893
https://juejin.cn/post/6847902214279659533
*/
type Student struct {
Name string `validate:required`
Email string `validate:"email"`
Age int `validate:"max=30,min=12"`
}
func main() {
en := en.New() //英文翻译器
zh := zh.New() //中文翻译器
// 第一个参数是必填,如果没有其他的语言设置,就用这第一个
// 后面的参数是支持多语言环境(
// uni := ut.New(en, en) 也是可以的
// uni := ut.New(en, zh, tw)
uni := ut.New(en, zh)
trans, _ := uni.GetTranslator("zh") //获取需要的语言
student := Student{
Name: "tom",
Email: "testemal",
Age: 40,
}
validate := validator.New()
zhtrans.RegisterDefaultTranslations(validate, trans)
err := validate.Struct(student)
if err != nil {
// fmt.Println(err)
errs := err.(validator.ValidationErrors)
fmt.Println(removeStructName(errs.Translate(trans)))
}
}
func removeStructName(fields map[string]string) map[string]string {
result := map[string]string{}
for field, err := range fields {
result[field[strings.Index(field, ".") 1:]] = err
}
return result
}
在线代码[1]
输出:
map[Age:Age必须小于或等于30 Email:Email必须是一个有效的邮箱]
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"`
Email string `validate:"required,email"`
Test string `validate:"len=0|min=6,max=24,len=0|alphanum"` // 或者 条件之一, 使用|。 但每个,都是独立的一个逻辑,之间是&的关系(有一个不满足就报错"Error:Field validation"),且条件没有传递,所以要在alphanum前面也加一个len=0|。 而max=24和长度为0不冲突,所以不需要加
Products []CreateOrderProduct `validate:"min=1"` // 产品列表
}
type CreateOrderProduct struct {
SkuCode string `json:"skuCode"` // sku编码
Quantity int64 `json:"quantity"` // 商品数量
}
// oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,validate:"oneof=red green"
// https://www.cnblogs.com/jiujuan/p/13823864.html
func main() {
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 115,
Email: "Badger.Smith@gmail.com",
Test: "",
Products: []CreateOrderProduct{},
}
validate := validator.New()
err := validate.Struct(user)
if err != nil {
fmt.Println("=== error msg ====")
fmt.Println(err)
//if _, ok := err.(*validator.InvalidValidationError); ok {
// fmt.Println(err)
// return
//}
//
//fmt.Println("rn=========== error field info ====================")
//for _, err := range err.(validator.ValidationErrors) {
// // 列出效验出错字段的信息
// fmt.Println("Namespace: ", err.Namespace())
// fmt.Println("Fild: ", err.Field())
// 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("Type: ", err.Type())
// fmt.Println("Value: ", err.Value())
// fmt.Println("Param: ", err.Param())
// fmt.Println()
//}
// from here you can create your own error messages in whatever language you wish
return
}
}
在线代码[2]
输出:
代码语言:javascript复制=== error msg ====
Key: 'User.Products' Error:Field validation for 'Products' failed on the 'min' tag
跨字段验证
eqfield 同一结构体字段验证相等
eqfield=Field:必须等于 Field 的值
最常见的就是输入2次密码验证
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
// 多字段联合校验
// eqfield:同一结构体字段验证相等,最常见的就是输入2次密码验证
type Account struct {
Name string `validate:"lte=16"`
Age int `validate:"min=20"`
Password string `validate:"min=8"`
Password2 string `validate:"eqfield=Password"`
}
func main() {
account := &Account{
Name: "Badger",
Age: 115,
Password: "qwert12345",
Password2: "111111",
}
validate := validator.New()
err := validate.Struct(account)
if err != nil {
fmt.Println("=== error msg ====")
fmt.Println(err)
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
fmt.Println("rn=========== error field info ====================")
for _, err := range err.(validator.ValidationErrors) {
// 列出效验出错字段的信息
fmt.Println("Namespace: ", err.Namespace())
fmt.Println("Fild: ", err.Field())
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("Type: ", err.Type())
fmt.Println("Value: ", err.Value())
fmt.Println("Param: ", err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
}
在线运行[3]
输出:
代码语言:javascript复制=== error msg ====
Key: 'Account.Password2' Error:Field validation for 'Password2' failed on the 'eqfield' tag
=========== error field info ====================
Namespace: Account.Password2
Fild: Password2
StructNamespace: Account.Password2
StructField: Password2
Tag: eqfield
ActualTag: eqfield
Kind: string
Type: string
Value: 111111
Param: Password
nefield:同一结构体字段验证不相等
nefield=Field:必须不等于 Field 的值
例如,验证密码不能和用户名相同
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
// 多字段联合校验
// eqfield:同一结构体字段验证相等,最常见的就是输入2次密码验证
type Account struct {
Name string `validate:"lte=16"`
Age int `validate:"min=20"`
Password string `validate:"min=1,nefield=Name"`
}
func main() {
account := &Account{
Name: "Badger",
Age: 115,
Password: "Badger",
}
validate := validator.New()
err := validate.Struct(account)
if err != nil {
fmt.Println("=== error msg ====")
fmt.Println(err)
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
fmt.Println("rn=========== error field info ====================")
for _, err := range err.(validator.ValidationErrors) {
// 列出效验出错字段的信息
fmt.Println("Namespace: ", err.Namespace())
fmt.Println("Fild: ", err.Field())
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("Type: ", err.Type())
fmt.Println("Value: ", err.Value())
fmt.Println("Param: ", err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
}
输出:
代码语言:javascript复制=== error msg ====
Key: 'Account.Password' Error:Field validation for 'Password' failed on the 'nefield' tag
=========== error field info ====================
Namespace: Account.Password
Fild: Password
StructNamespace: Account.Password
StructField: Password
Tag: nefield
ActualTag: nefield
Kind: string
Type: string
Value: Badger
Param: Name
类似的还有
- gtfield=Field:必须大于 Field 的值。
- gtefield=Field: 必须大于等于 Field 的值。
- ltfield=Field:必须小于 Field 的值。
- ltefield=Field:必须小于等于 Field 的值。
eqcsfield=Other.Field:必须等于 struct Other 中 Field 的值。
用于验证跨结构体的两个字段是否相等,需要指定另一个字段的名称或路径作为参数,比如 eqcsfield=Other.Field 中的 Other.Field 就是指定的另一个字段。
在使用该选项时,会比较当前字段和指定的另一个字段的值是否相等,如果相等则验证通过,否则验证失败。这个选项通常用于验证密码和确认密码等类似的场景。
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Struct1 struct {
Field1 string `validate:"eqcsfield=Struct2.Field2""`
Struct2 struct {
Field2 string
}
}
func main() {
s := &Struct1{
Field1: "必须一致",
Struct2: struct{ Field2 string }{Field2: "没有一致"},
}
validate := validator.New()
err := validate.Struct(s)
if err != nil {
fmt.Println("=== error msg ====")
fmt.Println(err)
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
fmt.Println("rn=========== error field info ====================")
for _, err := range err.(validator.ValidationErrors) {
// 列出效验出错字段的信息
fmt.Println("Namespace: ", err.Namespace())
fmt.Println("Fild: ", err.Field())
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("Type: ", err.Type())
fmt.Println("Value: ", err.Value())
fmt.Println("Param: ", err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
}
输出:
代码语言:javascript复制=== error msg ====
Key: 'Struct1.Field1' Error:Field validation for 'Field1' failed on the 'eqcsfield' tag
=========== error field info ====================
Namespace: Struct1.Field1
Fild: Field1
StructNamespace: Struct1.Field1
StructField: Field1
Tag: eqcsfield
ActualTag: eqcsfield
Kind: string
Type: string
Value: 必须一致
Param: Struct2.Field2
看起来只支持嵌套结构体,不支持两个独立的结构体之间某个字段的比较
eqfield
和 eqcsfield
的区别在于它们用于比较的字段的位置不同:eqfield 比较的是同一个结构体中的两个字段的值,而 eqcsfield 比较的是当前结构体中的某个字段和另一个(子?)结构体中的字段的值
类似的还有
- 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 的值。
如何比较两个独立结构体中某两个字段的值?
required_with=Field1 Field2:在 Field1 或者 Field2 存在时,必须;
required_with=Field2:在 Field2被填写(即不为空)时,Field1也必须不能为空
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Name string `validate:"required"`
Email string `validate:"required_with=Phone"`
Phone string
}
func main() {
user1 := User{
Name: "John",
Email: "",
Phone: "",
}
user2 := User{
Name: "Mary",
Email: "mary@example.com",
Phone: "",
}
validate := validator.New()
err1 := validate.Struct(user1)
if err1 != nil {
fmt.Println(err1)
}
err2 := validate.Struct(user2)
if err2 != nil {
fmt.Println(err2)
}
}
验证通过~
在这个例子中,User 结构体包含 Name、Email 和 Phone 字段。Email 字段被标记为 required_with=Phone
,这意味着当 Phone 字段被填写时,Email 字段也必须被填写。
而如果把user1改为:
代码语言:javascript复制user1 := User{
Name: "John",
Email: "",
Phone: "123",
}
则会报错:
代码语言:javascript复制Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag
验证不通过
required_with_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须;(仅当所有其他指定的字段都存在时,验证字段才必须存在)
要么有这个tag的全部为空,如果有一个不为空,那所有其他的也都不能为空~
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User6 struct {
Name string `validate:"required"`
Email string `validate:"required_with_all=Phone"`
Phone string `validate:"required_with_all=Email"`
}
func main() {
user := User6{
Name: "John",
Email: "",
Phone: "",
}
validate := validator.New()
err := validate.Struct(user)
if err != nil {
fmt.Println(err)
}
}
Email 和 Phone 字段都被标记为 required_with_all,
这意味着当 Email 和 Phone
- 要么全都为空
- 如果其中的任何一个被填写时,另一个也必须被填写(不为空即可,可以不一样)
所以上面代码可以验证通过
如下也是合法的:
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User6 struct {
Name string `validate:"required"`
Email string `validate:"required_with_all=Phone"`
Phone string `validate:"required_with_all=Email"`
}
func main() {
user := User6{
Name: "John",
Email: "1",
Phone: "2",
}
validate := validator.New()
err := validate.Struct(user)
if err != nil {
fmt.Println(err)
}
}
类似的还有:
required_without=Field1 Field2:在 Field1 或者 Field2 不存在时,必须;
Field1 Field2字段其中(至少)一个为空,则当前字段不能为空
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Name string `validate:"required"`
Email string
Phone string
Address string `validate:"required_without=Email Phone"`// Email Phone 中间不能加逗号
}
func main() {
user1 := User{
Name: "John",
Email: "",
Phone: "",
Address: "123 Main St.",
}
user2 := User{
Name: "Mary",
Email: "mary@example.com",
Phone: "",
Address: "",
}
validate := validator.New()
err1 := validate.Struct(user1)
if err1 != nil {
fmt.Println("err1:", err1)
}
err2 := validate.Struct(user2)
if err2 != nil {
fmt.Println("err2:", err2)
}
}
输出:
代码语言:javascript复制err2: Key: 'User7.Address' Error:Field validation for 'Address' failed on the 'required_without' tag
User 结构体包含 Name、Email、Phone 和 Address 字段。Address 字段被标记为 required_without=Email Phone,这意味着当 Email 和 Phone 字段至少一个为空时,Address 字段必须被填写。
required_without_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须; (仅当所有其他指定字段都不存在时,验证字段才必须...)
Field1 Field2字段都为空时,则当前字段不能为空
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User7 struct {
Name string `validate:"required"`
Email string
Phone string
Address string `validate:"required_without_all=Email Phone"`
}
func main() {
user1 := User7{
Name: "John",
Email: "",
Phone: "111",
Address: "123 Main St.",
}
user2 := User7{
Name: "Mary",
Email: "",
Phone: "",
Address: "",
}
validate := validator.New()
err1 := validate.Struct(user1)
if err1 != nil {
fmt.Println("err1:", err1)
}
err2 := validate.Struct(user2)
if err2 != nil {
fmt.Println("err2:", err2)
}
}
输出:
代码语言:javascript复制err2: Key: 'User7.Address' Error:Field validation for 'Address' failed on the 'required_without_all' tag
验证proto
可参考 Go gRPC进阶-proto数据验证(九) [4]
更复杂的判断
代码语言:javascript复制type User struct {
Age uint8 validate:"gte=0,lte=130"
Email string validate:"required,email"
Score int validate:"min=1" // 分数
Gender string validate:"required,oneof=男 女"
}
如果满足以下任意一项则认为通过:
- Gender=男,Age小于35,Score大于60
- Gender=女,Age小于40,Score大于50
这种用validator/v10
能判断吗?..
这种复杂的验证规则超出了validator/v10
的基本功能,需要进行自定义验证函数。可以使用validator/v10的Func函数,通过编写自定义的验证函数来实现这种验证规则。
如下是一个示例代码:
代码语言:javascript复制package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Age uint8 `validate:"gte=0,lte=130,customValidation"`
Email string `validate:"required,email"`
Score int `validate:"required,gte=0,customValidation"`
Gender string `validate:"required,oneof=男 女,customValidation"`
}
func validateUser(fl validator.FieldLevel) bool {
user, ok := fl.Top().Interface().(*User)
fmt.Println("user is:", user)
if !ok {
return false
}
if user.Gender == "男" && user.Age < 35 && user.Score > 60 {
return true
}
if user.Gender == "女" && user.Age < 40 && user.Score > 50 {
return true
}
return false
}
// - Gender=男,Age小于35,Score大于60
// - Gender=女,Age小于40,Score大于50
func main() {
user := &User{
Age: 36,
Email: "example@gmail.com",
Score: 1711,
Gender: "男",
}
validate := validator.New()
err := validate.RegisterValidation("customValidation", validateUser)
if err != nil {
fmt.Println(err)
return
}
err = validate.Struct(user)
fmt.Println("err is:", err)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("validation succeeded")
}
输出:
代码语言:javascript复制user is: &{36 example@gmail.com 1711 男}
user is: &{36 example@gmail.com 1711 男}
user is: &{36 example@gmail.com 1711 男}
err is: Key: 'User.Age' Error:Field validation for 'Age' failed on the 'customValidation' tag
Key: 'User.Score' Error:Field validation for 'Score' failed on the 'customValidation' tag
Key: 'User.Gender' Error:Field validation for 'Gender' failed on the 'customValidation' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'customValidation' tag
Key: 'User.Score' Error:Field validation for 'Score' failed on the 'customValidation' tag
Key: 'User.Gender' Error:Field validation for 'Gender' failed on the 'customValidation' tag
参考资料:
golang之验证器validator[5]
【Go】数据验证-validator[6]
Go 使用validator进行后端数据校验[7]
gopkg.in/go-playground/validator.v10[8]
结构字段验证--validator.v9[9]
Golang验证器之validator使用详解[10]
Go 每日一库之 validator[11]
golang常用库:字段参数验证库-validator使用[12]
参考资料
[1]
在线代码: https://go.dev/play/p/p0r79v_RuSO
[2]
在线代码: https://go.dev/play/p/tjZ6PurLe-p
[3]
在线运行: https://go.dev/play/p/UNj1WhSY1fM
[4]
Go gRPC进阶-proto数据验证(九) : https://www.cnblogs.com/FireworksEasyCool/p/12761033.html
[5]
golang之验证器validator: https://www.cnblogs.com/xingxia/p/golang_validator.html
[6]
【Go】数据验证-validator: https://blog.csdn.net/qq_42887507/article/details/120934568
[7]
Go 使用validator进行后端数据校验: https://www.admpub.com/blog/post/admin/Go-使用validator进行后端数据校验
[8]
gopkg.in/go-playground/validator.v10: https://pkg.go.dev/gopkg.in/go-playground/validator.v10
[9]
结构字段验证--validator.v9: https://www.cnblogs.com/zhzhlong/p/10033234.html
[10]
Golang验证器之validator使用详解: https://www.jb51.net/article/260639.htm
[11]
Go 每日一库之 validator: https://darjun.github.io/2020/04/04/godailylib/validator/
[12]
golang常用库:字段参数验证库-validator使用: https://www.cnblogs.com/jiujuan/p/13823864.html