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版) 课程目标 给零基础同学快速了解扩散模型的核心模块,有个整体框架的理解。知道扩散模型的改进和设计的核心模块。 课程特色…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...