【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 validateimport ("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 必须为emailAddress *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.ValidationErrorsif 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 mainimport ("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 必填,且必须大于等于 1Username 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 检查是否包含 ValidationErrorsvar 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.rhkb.cn/news/8138.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis的分片集群模式

redis的分片集群模式 1 主从哨兵集群的问题和分片集群特点 主从哨兵集群可应对高并发写和高可用性,但是还有2个问题没有解决: (1)海量数据存储 (2)高并发写的问题 使用分片集群可解决,分片集群…

第一届“启航杯”网络安全挑战赛WP

misc PvzHE 去这个文件夹 有一张图片 QHCTF{300cef31-68d9-4b72-b49d-a7802da481a5} QHCTF For Year 2025 攻防世界有一样的 080714212829302316092230 对应Q 以此类推 QHCTF{FUN} 请找出拍摄地所在位置 柳城 顺丰 forensics win01 这个软件 云沙盒分析一下 md5 ad4…

ThreadLocal概述、解决SimpleDateFormat出现的异常、内存泄漏、弱引用、remove方法

①. ThreadLocal简介 ①. ThreadLocal是什么 ①. ThreadLocal本地线程变量,线程自带的变量副本(实现了每一个线程副本都有一个专属的本地变量,主要解决的就是让每一个线程绑定自己的值,自己用自己的,不跟别人争抢。通过使用get()和set()方法,获取默认值或将其值更改为当前线程…

蓝桥杯模拟算法:多项式输出

P1067 [NOIP2009 普及组] 多项式输出 - 洛谷 | 计算机科学教育新生态 这道题是一道模拟题&#xff0c;我们需要分情况讨论&#xff0c;我们需要做一下分类讨论 #include <iostream> #include <cstdlib> using namespace std;int main() {int n;cin >> n;for…

LLM - 大模型 ScallingLaws 的设计 100B 预训练方案(PLM) 教程(5)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145356022 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

深度解析:基于Vue 3的教育管理系统架构设计与优化实践

一、项目架构分析 1. 技术栈全景 项目采用 Vue 3 TypeScript Tailwind CSS 技术组合&#xff0c;体现了现代前端开发的三大趋势&#xff1a; 响应式编程&#xff1a;通过Vue 3的Composition API实现细粒度响应 类型安全&#xff1a;约60%的组件采用TypeScript编写 原子化…

计算机组成原理(2)王道学习笔记

数据的表示和运算 提问&#xff1a;1.数据如何在计算机中表示&#xff1f; 2.运算器如何实现数据的算术、逻辑运算&#xff1f; 十进制计数法 古印度人发明了阿拉伯数字&#xff1a;0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#…

(详细)Springboot 整合动态多数据源 这里有mysql(分为master 和 slave) 和oracle,根据不同路径适配不同数据源

文章目录 Springboot 整合多动态数据源 这里有mysql&#xff08;分为master 和 slave&#xff09; 和oracle1. 引入相关的依赖2. 创建相关配置文件3. 在相关目录下进行编码&#xff0c;不同路径会使用不同数据源 Springboot 整合多动态数据源 这里有mysql&#xff08;分为maste…

AI如何帮助解决生活中的琐碎难题?

引言&#xff1a;AI已经融入我们的日常生活 你有没有遇到过这样的情况——早上匆忙出门却忘了带钥匙&#xff0c;到了公司才想起昨天的会议资料没有打印&#xff0c;或者下班回家还在纠结晚饭吃什么&#xff1f;这些看似微不足道的小事&#xff0c;往往让人疲惫不堪。而如今&a…

一分钟搭建promehteus+grafana+alertmanager监控平台

为什么要自己搭建一个监控平台 平时进行后端开发&#xff0c;特别是微服务的后端可开发&#xff0c;一定少不了对接监控平台&#xff0c;但是平时进行一些小功能的测试又没有必要每次都手动安装那么多软件进行一个小功能的测试&#xff0c;这里我使用docker-compose搭建了一个…

深入MapReduce——计算模型设计

引入 通过引入篇&#xff0c;我们可以总结&#xff0c;MapReduce针对海量数据计算核心痛点的解法如下&#xff1a; 统一编程模型&#xff0c;降低用户使用门槛分而治之&#xff0c;利用了并行处理提高计算效率移动计算&#xff0c;减少硬件瓶颈的限制 优秀的设计&#xff0c…

前端【10】jQuery DOM 操作

目录 jquery捕获查取 获得内容 - text()、html() 以及 val() 获取属性 - attr() ​编辑 jQuery 修改/设置内容和属性 设置内容 - text()、html() 以及 val() 设置属性 - attr() jQuery添加元素 jQuery - 删除元素 前端【9】初识jQuery&#xff1a;让JavaScript变得更简…

进程控制的学习

目录 1.进程创建 1.1 fork函数 1.2 fork函数返回值 1.3 写时拷贝 1.4 fork 常规用法 1.5 fork 调用失败的原因 2. 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 2.2.1 从main 返回 2.2.2 echo $&#xff1f; 查看进程退出码 2.2.2.1 我们如何得到退出码代表的含…

数据结构与算法分析:专题内容——人工智能中的寻路7之AlphaBeta(代码详解)

一、算法描述 在考虑到对手的可能走法之后&#xff0c;Minimax算法能够较为恰当地找出玩家的最优走法。但是&#xff0c;在生成博弈树时&#xff0c;这个信息却没有使用&#xff01;我们看看早先介绍的BoardEvaluation评分函数。回忆一下下图Minimax的探测&#xff1a; 这是从…

12、本地缓存分布式缓存(未完待续)

1、哪些数据适合放入缓存&#xff1f; 即时性、数据一致性要求不高的访问量大且更新频率不高的数据&#xff08;读多&#xff0c;写少&#xff09; 2、本地缓存 1、本地缓存&#xff0c;如果是单体项目&#xff0c;部署到一台服务器上&#xff0c;就不存在什么问题&#xff…

Linux——网络基础(1)

文章目录 目录 文章目录 前言 一、文件传输协议 应用层 传输层 网络层 数据链路层 数据接收与解封装 主机与网卡 数据传输过程示意 二、IP和MAC地址 定义与性质 地址格式 分配方式 作用范围 可见性与可获取性 生活例子 定义 用途 特点 联系 四、TCP和UDP协…

免费GPU算力,不花钱部署DeepSeek-R1

在人工智能和大模型技术飞速发展的今天&#xff0c;越来越多的开发者和研究者希望能够亲自体验和微调大模型&#xff0c;以便更好地理解和应用这些先进的技术。然而&#xff0c;高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是&#xff0c;腾讯云Cloud Studio提供了免…

阿里前端开发规范

文章目录 1. 为什么前端写代码要规范&#xff1f;一、代码规范的必要性二、 规范带来的好处 2. 资源一、推荐 1. 为什么前端写代码要规范&#xff1f; 一、代码规范的必要性 可维护性 统一的代码风格便于理解和修改减少代码维护成本降低项目交接难度 团队协作 提高团队开发效…

Linux 小火车

1.添加epel软件源 2.安装sl 3. 安装完成后输入&#xff1a; sl

高效流式大语言模型(StreamingLLM)——基于“注意力汇聚点”的突破性研究

论文地址&#xff1a;https://arxiv.org/pdf/2309.17453 github地址&#xff1a;https://github.com/mit-han-lab/streaming-llm 1. 研究背景与挑战 随着大语言模型&#xff08;LLMs&#xff09;在对话系统、文档摘要、代码补全和问答等领域的广泛应用&#xff0c;如何高效且准…