gin参数验证
一. 结构体验证
用gin框架的数据验证,可以不用解析数据,减少if else。如下面的代码,如果需要增加判断条件,就需要增加if或者if else。
type MyApi struct {a intb string
}func checkMyApi(val *MyApi) bool {if val.a == 0 {return false}if val.b != "foo" && val.b != "bar" {return false}return true
}
1.1 binding标签
在 Go 语言中,结构体的binding标签用于指定结构体字段在进行数据绑定(如表单数据绑定或请求体绑定)时的规则和验证。用于gin验证器。
package mainimport ("fmt""net/http""github.com/gin-gonic/gin"
)type MyApi struct {A int `form:"a" binding:"required"`B string `form:"b" binding:"required,oneof=foo 'bar'"`C []float64 `form:"c" binding:"required,gt=0"`
}func main() {r := gin.Default()r.GET("/check", func(c *gin.Context) {var api MyApiif err := c.ShouldBind(&api); err != nil {c.String(http.StatusBadRequest, err.Error())return}str := fmt.Sprintf("a:%d, b:%s, c:%#v", api.A, api.B, api.C)c.String(http.StatusOK, str)})r.Run()
}
required:表示字段不能为对应类型的零值。
oneof:用于限制字段取值必须是指定的多个值中的一个,多个值之间使用空格分隔。如果字符串本身包含空格,可以使用单引号括起来。
gt表示greater than大于。对于数字,这将确保值大于给定的值。对于字符串,它检查字符串长度是否大于给定值。对于切片,数组和映射,验证元素的数量。

常用tag:
required:表示该字段是必需的,不能为空。
min:指定字段的最小值。
max:指定字段的最大值。
eq: 等于,如:binding:“eq=3”
ne: 不等于,如:binding:“ne=12”
gt: 大于
gte: 大于等于
lt: 小于
lte: 小于等于
eqfield: 等于其它字段,如; Password string `bingding:“eqfield=ConfirmPassword”` 表示密码和确认密码一致
nefield: 不等于其它字段
email:验证字段是否为有效的电子邮件地址。如:binding:“email”
url:验证字段是否为有效的 URL。如:binding:“url”
datetime:验证字段是否为有效的日期时间格式。
len:指定字段的长度。
default:指定字段的默认值。
omitempty:指定当字段为空时,绑定时忽略该字段。
oneof:枚举验证,如:binding:“oneof=man woman”, 只能为man或者woman
contains: 字符串验证,包含某字符串,如:binding:“contains=love”
excludes: 字符串验证,不包含某字符串, 如:binding:“contains=money”
startswith 字符串验证,字符串前缀
endswith:字符串验证,字符串后缀
pattern:使用正则表达式验证字段的值。
datetime: 日期格式验证,如:binding:“datetime=2006-01-02 15:04:05”,注:时间必须是2006年1月2号下午3点4分5秒,不可以修改年月日时分秒的值。
忽略字段:binding:“-”
1.2 dive的使用
先看一个实例的请求结构体:
type PostAttributeValuesReq struct {CreatorId string `binding:"required"` // 创建者IDValues []struct {Value string `binding:"required"` // 属性值Days uint // 天数(计费模式使用)} `binding:"required,gt=0"` // 属性值数组
}
假设使用json传参,发现Values.Value字段没有识别出来,也就是说切片元素struct字段的required并没有生效。
{"creatorId":"dablelv","values":[{}]
}
可以使用dive标签,这是告诉验证器深入到切片,数组或映射中,并使用元素标签来验证切片,数组或映射元素。
type PostAttributeValuesReq struct {CreatorId string `binding:"required"` // 创建者IDValues []struct {Value string `binding:"required"` // 属性值Days uint // 天数(计费模式使用)} `binding:"required,gt=0,dive"` // 属性值数组
}
1.3 validate标签
validate用于数据验证库的字段验证。
package mainimport ("fmt""net/http""github.com/gin-gonic/gin""github.com/go-playground/validator/v10"
)type MyApi struct {A int `form:"a" validate:"required"`B string `form:"b" validate:"required,oneof=foo 'bar'"`C []float64 `form:"c" validate:"required,gt=0"`
}func main() {r := gin.Default()r.GET("/check", func(c *gin.Context) {var api MyApiif err := c.ShouldBind(&api); err != nil {c.String(http.StatusBadRequest, err.Error())return}//数据验证validate := validator.New()if err := validate.Struct(api); err != nil {c.String(http.StatusBadRequest, err.Error())return}str := fmt.Sprintf("a:%d, b:%s, c:%#v", api.A, api.B, api.C)c.String(http.StatusOK, str)})r.Run()
}
validate的常用tag:https://github.com/go-playground/validator/blob/master/README.md
1.4 binding和validate区别
上下文:
- binding:主要用于Web框架的参数绑定,例如:Gin。在请求参数绑定到结构体字段时,验证数据。
- validate:主要用于数据验证库,例如:go-playground/validator。
错误处理:
- 在Web框架中,binding通常会导致框架返回HTTP 400 Bad Request错误,指示客户端请求参数不合法。
- 在数据验证库中,会在验证时产生相应的验证错误,开发者可根据需要进行特别处理。
使用场景:
- 适用于Web框架的参数绑定
- 适用于在通用的数据验证场景中
二. 自定义验证
使用go-playground/validator包。
2.1 validator包简介
validator包是Golang中一个非常受欢迎的数据验证工具,它提供了丰富的验证规则和简单易用的API。使用validator包可以轻松的定义和执行各种验证规则,如必填字段,最大长度,最小值等。同时validator包还支持自定义验证规则,可根据具体业务需求进行扩展。
validator包安装:
go get github.com/go-playground/validator/v10
2.2 基本使用

2.3 自定义规则验证
除了支持内置的验证规则,validator包还支持自定义验证规则。我们可以通过实现validator.Func类型的函数来定义自己的验证规则。
利用validator包,使binding注册自定义规则。
package mainimport ("net/http""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/validator/v10"
)type User struct {//2. 在参数binding上使用自定义的校验方法函数注册时的名称Name string `form:"name" binding:"NotNullAndAdmin"`Age int `form:"age" binding:"gte=0,lte=100"`Email string `form:"email" binding:"email"`
}// 1. 自定义校验方法
// func notNullAndAdmin(c *validator.Validate, topStruct reflect.Value, curStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func notNullAndAdmin(c validator.FieldLevel) bool {value := c.Field().String()//字段不能为空,并且不等于adminreturn value != "" && !(value == "admin")
}func main() {r := gin.Default()//3.将我们自定义的校验方法注册到validator中if v, ok := binding.Validator.Engine().(*validator.Validate); ok {v.RegisterValidation("NotNullAndAdmin", notNullAndAdmin)}r.GET("/check", func(c *gin.Context) {var u Userif err := c.ShouldBind(&u); err != nil {c.String(http.StatusBadRequest, err.Error())}c.String(http.StatusOK, "check pass")})r.Run()
}

利用validator包,使validate注册自定义规则。
package mainimport ("net/http""github.com/gin-gonic/gin""github.com/go-playground/validator/v10"
)type User struct {//2. 在参数binding上使用自定义的校验方法函数注册时的名称Name string `form:"name" validate:"NotNullAndAdmin"`Age int `form:"age" validate:"gte=0,lte=100"`Email string `form:"email" validate:"email"`
}// 1. 自定义校验方法
// func notNullAndAdmin(c *validator.Validate, topStruct reflect.Value, curStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func notNullAndAdmin(c validator.FieldLevel) bool {value := c.Field().String()//字段不能为空,并且不等于adminreturn value != "" && !(value == "admin")
}func main() {r := gin.Default()r.GET("/check", func(c *gin.Context) {var u Userif err := c.ShouldBind(&u); err != nil {c.String(http.StatusBadRequest, err.Error())}//3.将我们自定义的校验方法注册到validator中validate := validator.New()validate.RegisterValidation("NotNullAndAdmin", notNullAndAdmin)//数据校验err := validate.Struct(u)if err != nil {c.String(http.StatusBadRequest, err.Error())}c.String(http.StatusOK, "check pass")})r.Run()
}

原理是:
通过反射获取struct中的tag,根据不同的验证规则进行验证。
三. 多语言翻译验证
validator库本身使支持国际化的,可以借助相应的语言包实现多语言翻译验证。
验证器使用的是:
go get gopkg.in/go-playground/validator.v9
翻译器:
github.com/go-playground/universal-translator
验证器注册翻译器:
gopkg.in/go-playground/validator.v9/translations/en
gopkg.in/go-playground/validator.v9/translations/zh
gopkg.in/go-playground/validator.v9/translations/zh_tw
例如:当业务系统对验证信息有特殊需求时,返回信息需要自定义,手机端返回的信息需要时中文,而pc端发挥返回的信息需要是英文,如何做到请求一个借口满足上述三种情况。
package mainimport ("fmt""net/http""github.com/gin-gonic/gin""github.com/go-playground/locales/en""github.com/go-playground/locales/zh""github.com/go-playground/locales/zh_Hant_TW"ut "github.com/go-playground/universal-translator""gopkg.in/go-playground/validator.v9"en_translations "gopkg.in/go-playground/validator.v9/translations/en"zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw"
)var (Uni *ut.UniversalTranslatorValidate *validator.ValidateTrans ut.Translator
)type User struct {Name string `form:"name" validate:"required"`Tagline string `form:"tag_line" validate:"required,lt=10"`Tagline2 string `form:"tag_line2" validate:"required,gt=1"`
}func main() {en := en.New()zh := zh.New()zh_tw := zh_Hant_TW.New()Uni = ut.New(en, zh, zh_tw)Validate = validator.New()r := gin.Default()r.GET("/check", registerTranslation(), startPage)r.Run()
}func registerTranslation() gin.HandlerFunc {return func(c *gin.Context) {//获得参数locale := c.DefaultQuery("locale", "zh")//翻译器Trans, _ = Uni.GetTranslator(locale)//验证器注册翻译器switch locale {case "zh":zh_translations.RegisterDefaultTranslations(Validate, Trans)case "en":en_translations.RegisterDefaultTranslations(Validate, Trans)case "zh_tw":zh_tw_translations.RegisterDefaultTranslations(Validate, Trans)default:zh_translations.RegisterDefaultTranslations(Validate, Trans)}}
}func startPage(c *gin.Context) {//自定义错误内容Validate.RegisterTranslation("required", Trans, func(ut ut.Translator) error {return ut.Add("required", "{0} must have val!", true)}, func(ut ut.Translator, fe validator.FieldError) string {t, _ := ut.T("required", fe.Field())return t})//验证数据var u Userif err := c.ShouldBind(&u); err != nil {c.String(http.StatusBadRequest, err.Error())return}fmt.Println(u)if err := Validate.Struct(u); err != nil {errs := err.(validator.ValidationErrors)sliceErr := []string{}for _, e := range errs {//翻译错误sliceErr = append(sliceErr, e.Translate(Trans))}c.String(http.StatusOK, fmt.Sprintf("%#v", sliceErr))return}c.String(http.StatusOK, fmt.Sprintf("%#v", u))
}
演示:

相关文章:
gin参数验证
一. 结构体验证 用gin框架的数据验证,可以不用解析数据,减少if else。如下面的代码,如果需要增加判断条件,就需要增加if或者if else。 type MyApi struct {a intb string }func checkMyApi(val *MyApi) bool {if val.a 0 {retur…...
【web3】分享一个web入门学习平台-HackQuest
前言 一直想进入web3行业,但是没有什么途径,偶然在电鸭平台看到HackQuest的共学营,发现真的不错,并且还接触到了黑客松这种形式。 链接地址:HackQuest 平台功能 学习路径:平台有完整的学习路径ÿ…...
Sectigo或RapidSSL DV通配符SSL证书哪个性价比更高?
在当前的网络安全领域,选择一款合适的SSL证书对于保护网站和用户数据至关重要。Sectigo和RapidSSL作为市场上知名的SSL证书提供商,以其高性价比和快速的服务响应而受到市场的青睐。本文将对Sectigo和RapidSSL DV通配符证书进行深入对比,帮助用…...
金蝶云星空字段之间连续触发值更新
文章目录 金蝶云星空字段之间连续触发值更新场景说明具体需求:解决方案 金蝶云星空字段之间连续触发值更新 场景说明 字段A配置了字段B的计算公式,字段B配置了自动C的计算公式,修改A的时候,触发了B的重算,但是C触发不…...
Python 获取字典中的值(八种方法)
Python 字典(dictionary)是一种可变容器模型,可以存储任意数量的任意类型的数据。字典通常用于存储键值对,每个元素由一个键(key)和一个值(value)组成,键和值之间用冒号分隔。 以下是 Python 字典取值的几…...
Day49
Day49 代理模式proxy 概念: 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 代理模式分为静态代理和动态代理…...
OpenCV 车牌检测
OpenCV 车牌检测 级联分类器算法流程车牌检测相关链接 级联分类器 假设我们需要识别汽车图像中车牌的位置,利用深度学习目标检测技术可以采取基于锚框的模型,但这需要在大量图像上训练模型。 但是,级联分类器可以作为预训练文件直接使用&…...
机器学习/pytorch笔记:time2vec
1 概念部分 对于给定的标量时间概念 t,Time2Vec 的表示 t2v(t)是一个大小为 k1的向量,定义如下: 其中,t2v(t)[i]是 t2v(t)的第 i 个元素,F是一个周期性激活函数,ω和 ϕ是可学习的参数。 以下是个人理解&am…...
降低开关电源噪声的设计总结
开关电源的特征就是产生强电磁噪声,若不加严格控制,将产生极大的干扰。下面介绍的技术有助于降低开关电源噪声,能用于高灵敏度的模拟电路。 电路和器件的选择 一个关键点是保持dv/dt和di/dt在较低水平,有许多电路通过减小dv/dt和…...
rust嵌入式开发2024
老的rust embedded book 其实过时了. 正确的姿势是embassy 入手. 先说下以前rust写嵌入怎么教学小白的. 第一步,从这里 svd2rust 工具,自己生成库第二部,有了这个库,相当于就有了pac外设访问文件,然后其实就可以搞起来了. 那么为啥不好搞了. 因为太乱了. 小白喜欢你告我咋弄…...
字符串
对应练习题:力扣平台 14. 最长公共前缀 class Solution { public:string longestCommonPrefix(vector<string>& strs) {string strs1strs[0];//初始前缀字符串for (int i 1; i < strs.size(); i) {while(strs[i].find(strs1)!0)//遍历找到共同最长前…...
mysql8 锁表与解锁
方法1不行,就按方法2来执行; (一) 解锁方法1 连接mysql ,直接执行UNLOCK TABLES,细节如下: – 查询是否锁表 SHOW OPEN TABLES WHERE in_use >0 ; – 查询进程 show processlist ; – 查询到相对应的进程…...
第2篇 区块链的历史和发展:从比特币到以太坊
想象一下,你住在一个小镇上,每个人都有一个大账本,记录着所有的交易。这个账本很神奇,每当有人买卖东西,大家都会在自己的账本上记一笔,确保每个人的账本都是一致的。这就是区块链的基本思想。而区块链的故…...
从理论到实践的指南:企业如何建立有效的EHS管理体系?
企业如何建立有效的EHS管理体系?对于任何企业,没有安全就谈不上稳定生产和经济效益,因此建立EHS管理体系是解决企业长期追求的建立安全管理长效机制的最有效手段。良好的体系运转,可以最大限度地减少事故发生。 这篇借着开头这个…...
内网和外网的区别及应用
内网和外网的区别及应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来探讨一下计算机网络中的内网和外网,它们的区别以及在实际应用中的…...
电驱失效类型和风险分析,如何用精益思维提升电驱可靠性?
在电动车日益普及的今天,电驱系统作为电动车的“心脏”,其可靠性直接关系到整车的性能与用户体验。然而,电驱失效问题却一直困扰着电动车行业,如何提升电驱可靠性成为了业内关注的焦点。今天,深圳天行健精益管理咨询公…...
自动扫描范围在减少剂量多相CT肝脏成像中的应用:基于CNN和高斯模型| 文献速递-深度学习自动化疾病检查
Title 题目 Automatic scan range for dose-reduced multiphase CT imaging of theliver utilizing CNNs and Gaussian models 自动扫描范围在减少剂量多相CT肝脏成像中的应用:基于CNN和高斯模型 01 文献速递介绍 肝癌是全球癌症死亡的第四大原因,每…...
【机器学习】基于层次的聚类方法:理论与实践
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 基于层次的聚类方法:理论与实践引言1. 层次聚类基础1.1 概述1.2 距离…...
C# 验证PDF数字签名的有效性
数字签名作为PDF文档中的重要安全机制,不仅能够验证文件的来源,还能确保文件内容在传输过程中未被篡改。然而,如何正确验证PDF文件的数字签名,是确保文件完整性和可信度的关键。本文将详细介绍如何使用免费.NET控件通过C#验证PDF签…...
2小时动手学习扩散模型(pytorch版)【入门版】【代码讲解】
2小时动手学习扩散模型(pytorch版) 课程地址 2小时动手学习扩散模型(pytorch版) 课程目标 给零基础同学快速了解扩散模型的核心模块,有个整体框架的理解。知道扩散模型的改进和设计的核心模块。 课程特色…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
