当前位置: 首页 > article >正文

【Validator】字段验证器struct与多层级验证,go案例

标签用法总结表

标签功能代码实例
required字段必填Name string \v:“required”``
alphaunicode验证字段是否只包含字母和 Unicode 字符Name string \v:“alphaunicode”``
gte验证字段值是否大于等于指定值Age uint8 \v:“gte=10”``
lte验证字段值是否小于等于指定值Age uint8 \v:“lte=130”``
e164验证电话号码是否符合 E.164 国际格式Phone string \v:“e164”``
email验证字段是否为有效的电子邮件地址Email string \v:“email”``
iscolor验证字段是否为有效的颜色值(如 #ff0000FavouriteColor1 string \v:“iscolor”``
hexcolor验证字段是否为十六进制颜色值(如 #ffffffFavouriteColor2 string \v:“hexcolor”``
`rgbrgbahsl
dive对切片或 map 的每个元素进行递归验证Hobby []string \v:“dive,required”``
keys验证 map 的键值Data map[string]string \v:“dive,keys,alpha,len=2,endkeys”``
required_without_all如果指定字段都为空,则当前字段必须存在Phone string \v:“required_without_all=Email Address,omitempty”``
omitempty当字段为空时,跳过后续验证Age uint8 \v:“omitempty,gte=10,lte=130”``
len验证字段的长度是否等于指定值Data map[string]string \v:“dive,keys,alpha,len=2,endkeys”\ ``
endkeys结束对 map 键的验证Data map[string]string \v:“dive,keys,alpha,len=2,endkeys,required”``

标签参考:https://github.com/go-playground/validator?tab=readme-ov-file


示例代码

package validate

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

type User struct { // validate.SetTagName("v")在这里,结构体的标签开头起作用
	Name            string            `v:"required, alphaunicode"`
	Age             uint8             `v:"gte=10, lte=130"` // gte=10: 年龄必须大于等于 10。lte=130: 年龄必须小于等于 130。
	Phone           string            `v:"required,e164"`   // e164: 电话号码必须符合 E.164 格式(国际电话号码格式)
	Email           string            `v:"required,email"`
	FavouriteColor1 string            `v:"iscolor"`                    // 必须是一个有效的颜色值(例如:#ff0000)
	FavouriteColor2 string            `v:"hexcolor|rgb|rgba|hsl|hsla"` // 必须是十六进制颜色、RGB、RGBA、HSL 或 HSLA 颜色格式之一
	Address         *Address          `v:"required"`
	ContactUser     []*ContactUser    `v:"required,gte=1,dive"`                                                      // dive: 对列表中的每个元素进行递归验证,后面如果继续跟逻辑运算,则处理对象是dive递归的元素
	Hobby           []string          `v:"required,gte=2,dive,required,gte=2,alphaunicode"`                          // dive: 对列表中的每个元素进行递归验证
	Data            map[string]string `v:"required,gte=2,dive,keys,alpha,len=2,endkeys,required,gte=2,alphaunicode"` // keys,alpha,len=2: 键必须是字母且长度为 2。 endkeys: 结束对键的验证
	// required,gte=2,alphaunicode 值不能为空且必须包含至少 2 个字符,且只能是字母、数字或 Unicode 字符
}

type ContactUser struct {
	Name    string   `v:"required,alphaunicode"`
	Age     uint8    `v:"gte=10,lte=130"`                                     // omitempty,代表当字段为空,不会出现在输出中
	Phone   string   `v:"required_without_all=Email Address,omitempty,e164"`  // required_without_all 无需全部,即 如果 Phone 和 Email 都为空,则 Phone 和 Email 都必须填写,否则,Phone 可以为空。
	Email   string   `v:"required_without_all=Phone Address,omitempty,email"` //  如果 Phone 和 Address 字段都为空,则 Email 必须存在;否则,Email 可以为空。 email 必须为email
	Address *Address `v:"required_without_all=Phone Email"`
}

type Address struct {
	Province string `v:"required"`
	City     string `v:"required"`
}

func StructValidate() {
	addr := &Address{
		Province: "湖南",
		City:     "长沙",
	}
	contactUser1 := &ContactUser{
		Name:  "nick",
		Age:   18,
		Phone: "+861380013800",
	}
	contactUser2 := &ContactUser{
		Name:  "张三",
		Age:   18,
		Email: "nick@0voice.com",
	}
	user := &User{
		Name:            "nick",
		Age:             18,
		Phone:           "+861380013800",
		Email:           "nick@0voice.com",
		FavouriteColor1: "#ffff",
		FavouriteColor2: "rgb(255,255,255)",
		Address:         addr,
		ContactUser:     []*ContactUser{contactUser1, contactUser2},
		Hobby:           []string{"篮球", "羽毛球"},
		Data:            map[string]string{"AB": "篮球", "CD": "羽毛球"},
	}
	err := validate.Struct(user)
	if err != nil {
		//if errors, ok := err.(validator.ValidationErrors); ok {
		// 错误的原因在于 validate.Struct(user) 返回的错误并不是直接的
		//validator.ValidationErrors 类型,而是可能被包装在其他类型的错误中。
		//因此,直接进行类型断言会失败。

		// 使用 errors.As 函数来检查是否包含 validator.ValidationErrors 类型的错误。
		var validationErrors validator.ValidationErrors
		if errors.As(err, &validationErrors) { // errors.As 函数的第二个参数需要是一个指针,指向一个可以接收错误类型的变量。
			// 因此,一定要先var一个可以接受的,然后再使用errors.As,而不是直接if errors.As(err, &validator.ValidationErrors)
			for _, err := range validationErrors {
				fmt.Println(err)
			}
		} else {
			fmt.Println("Validation failed with unknown error:", err)
		}
	}
}


示例 1: 必填字段验证
Name string `v:"required,alphaunicode"`
  • requiredName 字段不能为空。
  • alphaunicodeName 只能包含字母或 Unicode 字符。

示例 2: 数值范围验证
Age uint8 `v:"gte=10,lte=130"`
  • gte=10Age 必须大于等于 10。
  • lte=130Age 必须小于等于 130。

示例 3: 列表递归验证
Hobby []string `v:"required,gte=2,dive,required,gte=2,alphaunicode"`
  • requiredHobby 列表不能为空。
  • gte=2Hobby 列表必须包含至少 2 个元素。
  • dive:对列表中的每个元素进行递归验证。
  • required,gte=2,alphaunicode:列表中的每个元素必须非空,且至少为 2 个字符,且只包含字母和 Unicode 字符。

示例 4: Map 验证
Data map[string]string `v:"required,gte=2,dive,keys,alpha,len=2,endkeys,required,gte=2,alphaunicode"`
  • required,gte=2Data 必须有至少 2 个键值对。
  • dive:递归验证 map 的每个键和值。
  • keys,alpha,len=2,endkeys:键必须为字母,且长度为 2。
  • required,gte=2,alphaunicode:值不能为空,至少为 2 个字符,且只能包含字母和 Unicode 字符。

Go 语言中 omitemptyrequired_without_all 的合理使用

1. omitempty 标签的作用

omitemptyjson 标签的一个选项,用于控制 JSON 序列化时的行为。当字段的值为零值(如空字符串、0、nil 等)时,该字段将被忽略,不会出现在生成的 JSON 输出中。

示例:

type User struct {
    Name string `json:"name,omitempty"`
    Age  int    `json:"age,omitempty"`
}
  • 如果 NameAge 字段为空或为零值,则它们不会出现在 JSON 输出中。
user := User{
    Name: "Alice",
    Age:  0,
}

jsonData, _ := json.Marshal(user)
fmt.Println(string(jsonData)) // 输出: {"name":"Alice"}
2. required_without_all 标签的作用

required_without_allvalidator 标签的一部分,用于条件验证。它表示当指定的其他字段都为空时,当前字段必须存在且不为空。

示例:

type ContactUser struct {
    Phone   string `v:"required_without_all=Email Address,e164"`
    Email   string `v:"required_without_all=Phone Address,email"`
    Address *Address `v:"required_without_all=Phone Email"`
}
  • 如果 PhoneEmail 都为空,则 Phone 必须存在且符合 E.164 格式。
  • 如果 PhoneAddress 都为空,则 Email 必须存在且是有效的电子邮件地址。
  • 如果 PhoneEmail 字段有一个非空,则另一个字段可以为空。
3. omitemptyrequired_without_all 的组合使用

这两个标签可以共存,但需要注意它们分别作用于不同的阶段:

  • required_without_all 用于数据验证,确保在特定条件下字段不为空。
  • omitempty 用于 JSON 序列化,确保零值字段不会出现在输出中。

示例:

type ContactUser struct {
    Name    string   `json:"name,omitempty" v:"required,alphaunicode"`
    Age     uint8    `json:"age,omitempty" v:"gte=10,lte=130"`
    Phone   string   `json:"phone,omitempty" v:"required_without_all=Email Address,e164"`
    Email   string   `json:"email,omitempty" v:"required_without_all=Phone Address,email"`
    Address *Address `json:"address,omitempty" v:"required_without_all=Phone Email"`
}
  • 验证逻辑required_without_all 确保在特定条件下字段不为空。
  • JSON 序列化omitempty 确保在 JSON 序列化时,零值字段不会出现在输出中。
4. 最佳实践建议

为了提高代码的可读性和维护性,建议将 json 标签和 validator 标签分开写:

type ContactUser struct {
    Name    string   `json:"name,omitempty" v:"required,alphaunicode"`
    Age     uint8    `json:"age,omitempty" v:"gte=10,lte=130"`
    Phone   string   `json:"phone,omitempty" v:"required_without_all=Email Address,e164"`
    Email   string   `json:"email,omitempty" v:"required_without_all=Phone Address,email"`
    Address *Address `json:"address,omitempty" v:"required_without_all=Phone Email"`
}

多层struct的go案例讲解

package main

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

// 嵌套的地址结构
type Address struct {
	Street  string `validate:"required"`       // 街道名称必填
	City    string `validate:"required,alpha"` // 城市名称必填,只能包含字母
	ZipCode string `validate:"required,len=5"` // 邮编必填,且长度必须为 5
}

// 嵌套的联系人结构
type Contact struct {
	Name    string  `validate:"required,alphaunicode"`        // 名称必填,支持字母和 Unicode 字符
	Phone   string  `validate:"required_without=Email,e164"`  // 电话和邮箱至少填一个,且电话需符合 E.164 格式
	Email   string  `validate:"required_without=Phone,email"` // 如果没有填写电话,邮箱必须是有效的邮件地址
	Address Address `validate:"required,dive"`                // 验证嵌套的地址字段
}

// 主用户结构
type User struct {
	ID       int       `validate:"required,gte=1"`                   // 用户 ID 必填,且必须大于等于 1
	Username string    `validate:"required,alphanum"`                // 用户名必填,只能包含字母和数字
	Contacts []Contact `validate:"required,gte=1,dive"`              // 至少包含一个联系人,每个联系人需通过验证
	Hobbies  []string  `validate:"required,gte=1,dive,alphaunicode"` // 至少有一个爱好,每个爱好只能包含字母和 Unicode 字符
}

var validate = validator.New()

func main() {
	// 构造测试数据
	user := User{
		ID:       101,
		Username: "john_doe",
		Contacts: []Contact{
			{
				Name:  "Alice",
				Phone: "+1234567890",
				Address: Address{
					Street:  "123 Main St",
					City:    "Springfield",
					ZipCode: "12345",
				},
			},
			{
				Name:  "Bob",
				Email: "bob@example.com",
				Address: Address{
					Street:  "456 Elm St",
					City:    "Shelbyville",
					ZipCode: "54321",
				},
			},
		},
		Hobbies: []string{"Reading", "Cooking"},
	}

	// 进行验证
	err := validate.Struct(user)
	if err != nil {
		// 使用 errors.As 检查是否包含 ValidationErrors
		var validationErrors validator.ValidationErrors // 底层是一个切片
		if errors.As(err, &validationErrors) {
			for _, fieldErr := range validationErrors {
				fmt.Printf("Field '%s' failed validation: %s\n", fieldErr.Field(), fieldErr.Tag())
			}
		} else {
			fmt.Println("Validation failed:", err)
		}
	} else {
		fmt.Println("All validations passed!")
	}
}

1. 嵌套结构
  • AddressContact 是嵌套结构:
  • Address 包含 StreetCityZipCode,验证逻辑应用到每个字段。
  • Contact 包含 NamePhoneEmail 和嵌套的 Address,同时对 PhoneEmail 设置了互斥验证。
2. 多层级验证
  • 一级验证UserIDUsername
  • 二级验证UserContacts 列表:
  • 验证列表中至少包含一个联系人(gte=1)。
  • 使用 dive,递归验证每个联系人。
  • 三级验证Contact 中的 Address
  • 验证 StreetCityZipCode
3. 验证列表和递归验证
  • Contacts 使用 dive 标签递归验证列表内的每个元素。
  • Hobbies 列表的验证中:
  • 使用 dive 验证列表中的每个爱好,确保它们符合 alphaunicode 的规则。
4. 动态互斥规则
  • PhoneEmail 互斥规则:
  • Phonerequired_without=Email,表示如果 Email 为空,Phone 必须存在。
  • Emailrequired_without=Phone,表示如果 Phone 为空,Email 必须是有效邮件地址。

https://github.com/0voice


http://www.kler.cn/a/524062.html

相关文章:

  • js小游戏---2048(附源代码)
  • 展示统计信息收集情况
  • [权限提升] 常见提权的环境介绍
  • 计算机网络 IP 网络层 2 (重置版)
  • 大一计算机的自学总结:异或运算
  • 【Rust自学】15.7. 循环引用导致内存泄漏
  • ReentrantLock锁江湖:一柄寒刃镇并发纷争
  • ts 基础核心
  • 2025-01-28 - 通用人工智能技术 - RAG - 本地安装 DeepSeek-R1对话系统 - 流雨声
  • 拟合损失函数
  • C语言练习(29)
  • PWM频率测量方法
  • langchain基础(二)
  • 【数据结构】_链表经典算法OJ:分割链表(力扣—中等)
  • 信息学奥赛一本通 1390:食物链【NOI2001】| 洛谷 P2024 [NOI2001] 食物链
  • 通过 NAudio 控制电脑操作系统音量
  • 8638 直接插入排序
  • 9.7 打造你的专属智能助手:基于 GPT Builder 定制化 ChatGPT 应用全指南
  • 智能客服系统:结合 AI 模型与数据库实现对话与知识检索
  • FastAPI + GraphQL + SQLAlchemy 实现博客系统
  • DearMom婴儿车:书籍点亮希望,为乡村留守儿童架起知识桥梁
  • 【1.安装ubuntu22.04】
  • (四)线程 和 进程 及相关知识点
  • postgres基准测试工具pgbench如何使用自定义的表结构和自定义sql
  • Autogen_core:Concurrent Agents
  • 出现 Error processing condition on org.springframework.cloud.openfeign 解决方法