<br>
项目地址: github.com/go-playground/validator/v10
<br>
单字段校验
<br>
对前端传参进行校验
单字段多个条件 校验
<br>
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
}
复制代码
在线代码
输出:
map[Age:Age必须小于或等于30 Email:Email必须是一个有效的邮箱]
<br>
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("\r\n=========== 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
}
}
复制代码
在线代码
输出:
=== error msg ====
Key: 'User.Products' Error:Field validation for 'Products' failed on the 'min' tag
复制代码
<br>
<br>
跨字段验证
<br>
eqfield 同一结构体字段验证相等
<br>
eqfield=Field:必须等于 Field 的值
最常见的就是输入 2 次密码验证
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("\r\n=========== 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
}
}
复制代码
在线运行
输出:
=== 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
复制代码
<br>
nefield:同一结构体字段验证不相等
<br>
nefield=Field:必须不等于 Field 的值
例如,验证密码不能和用户名相同
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("\r\n=========== 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
}
}
复制代码
输出:
=== 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
复制代码
<br>
类似的还有
gtfield=Field:必须大于 Field 的值。
gtefield=Field: 必须大于等于 Field 的值。
ltfield=Field:必须小于 Field 的值。
ltefield=Field:必须小于等于 Field 的值。
<br>
<br>
eqcsfield=Other.Field:必须等于 struct Other 中 Field 的值。
<br>
用于验证跨结构体的两个字段是否相等,需要指定另一个字段的名称或路径作为参数,比如 eqcsfield=Other.Field 中的 Other.Field 就是指定的另一个字段。
在使用该选项时,会比较当前字段和指定的另一个字段的值是否相等,如果相等则验证通过,否则验证失败。这个选项通常用于验证密码和确认密码等类似的场景。
<br>
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("\r\n=========== 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
}
}
复制代码
输出:
=== 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
复制代码
看起来只支持嵌套结构体,不支持两个独立的结构体之间某个字段的比较
<br>
eqfield
和 eqcsfield
的区别在于它们用于比较的字段的位置不同:eqfield 比较的是同一个结构体中的两个字段的值,而 eqcsfield 比较的是当前结构体中的某个字段和另一个(子?)结构体中的字段的值
<br>
类似的还有
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 的值。
<br>
如何比较两个独立结构体中某两个字段的值?
<br>
<br>
required_with=Field1 Field2:在 Field1 或者 Field2 存在时,必须;
required_with=Field2:在 Field2 被填写(即不为空)时,Field1 也必须不能为空
<br>
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 改为:
user1 := User{
Name: "John",
Email: "",
Phone: "123",
}
复制代码
则会报错:
Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required_with' tag
复制代码
验证不通过
<br>
<br>
required_with_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须;(仅当所有其他指定的字段都存在时,验证字段才必须存在)
<br>
要么有这个 tag 的全部为空,如果有一个不为空,那所有其他的也都不能为空~
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
所以上面代码可以验证通过
<br>
如下也是合法的:
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)
}
}
复制代码
<br>
类似的还有:
required_without=Field1 Field2:在 Field1 或者 Field2 不存在时,必须;
Field1 Field2 字段其中(至少)一个为空,则当前字段不能为空
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)
}
}
复制代码
输出:
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 字段必须被填写。
<br>
required_without_all=Field1 Field2:在 Field1 与 Field2 都存在时,必须; (仅当所有其他指定字段都不存在时,验证字段才必须...)
<br>
Field1 Field2 字段都为空时,则当前字段不能为空
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)
}
}
复制代码
输出:
err2: Key: 'User7.Address' Error:Field validation for 'Address' failed on the 'required_without_all' tag
复制代码
<br>
<br>
验证 proto
可参考 Go gRPC进阶-proto数据验证(九)
<br>
<br>
更复杂的判断
<br>
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=男 女"
}
复制代码
如果满足以下任意一项则认为通过:
这种用validator/v10
能判断吗?..
<br>
这种复杂的验证规则超出了validator/v10
的基本功能,需要进行自定义验证函数。可以使用 validator/v10 的 Func 函数,通过编写自定义的验证函数来实现这种验证规则。
如下是一个示例代码:
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")
}
复制代码
输出:
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
复制代码
<br>
<br>
参考资料:
golang之验证器validator
【Go】数据验证-validator
Go 使用validator进行后端数据校验
gopkg.in/go-playground/validator.v10
结构字段验证--validator.v9
Golang验证器之validator使用详解
Go 每日一库之 validator
golang常用库:字段参数验证库-validator使用
<br>
有空还想探究下,这么一个工具怎么可以讲这么多期..
B站视频:论一款强大的验证组件在web开发中的重要性
B站视频:go语言验证框架Validator的6种高端操作
B站视频:go开源验证框架validator必会的3个操作
B站视频:validator验证框架3种自定义验证方法
B站视频:validator快速实现字段格式的验证
B站视频:validator快速搞定字段格式验证
B站视频:validator自定义验证与本地化提示
评论