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

Centos7网络配置(设置固定ip)
文章目录 1进入虚拟机设置选中【网络适配器】选择【NAT模式】2 进入windows【控制面板\网络和 Internet\网络和共享中心\更改适配器设置】设置网络状态。3 设置VM的【虚拟网络编辑器】4 设置系统网卡5 设置虚拟机固定IP 刚安装完系统,有的人尤其没有勾选自动网络配置…...

英伟达被“压制”的25年
十九世纪中叶的美国西部,掀起了一场轰轰烈烈的淘金热,但最终赚到钱的,并不是拿命去赌的淘金者。一个名叫萨姆布瑞南的商人,通过向淘金者出售铲子,成了加州历史上第一位百万富翁。 每一次风口出现时,总有企…...

windows安装Gitblit还是Bonobo Git Server
Gitblit 和 Bonobo Git Server 都是用于托管Git仓库的工具,但它们是基于不同平台的不同软件。 Gitblit 是一个纯 Java 写的服务器,支持托管 Git,Mercurial 和 SVN 仓库。它需要 Java 运行环境,适合在 Windows、Linux 和 Mac 平台…...

仪器校准的概念与定义,计量校准是什么?
仪器校准的定义,在之前所颁布的《国际计量学词汇 基础和通用概念及相关术语》文件中,已经有了明确说明,而该文件做了修改以后,在后续新的定义中,仪器校准具体被分为两部分,第一步是将被计量仪器和计量校准的…...

Vue3+Pinia
1.单纯调接口(安装pinia及引入如下第一张图) 1.npm install pinia2.在main.js里引入即可import { createPinia } from piniaapp.use(createPinia()) 1.stores建立你文件的ts、内容如下:1-1 import { defineStore } from pinia1-2 import { findPageJobSet } from …...

label studio数据标注平台的自动化标注使用
(作者:陈玓玏) 开源项目,欢迎star哦,https://github.com/data-infra/cube-studio 做图文音项目过程中,我们通常会需要进行数据标注。label studio是一个比较好上手的标注平台,可以直接搜索…...

高并发场景下的热点key问题探析与应对策略
目录 一、问题描述 二、发现机制 三、解决策略分析 (一)解决策略一:多级缓存策略 客户端本地缓存 代理节点本地缓存 (二)解决策略二:多副本策略 (三)解决策略三:热点…...

学习一下C++中的枚举的定义
目录 普通枚举 强类型枚举 普通枚举 枚举类型在C中是通过关键字enum来定义的。下面是一个简单的例子: enum Color { RED, GREEN, BLUE }; 在这个例子中,我们定义了一个名为Color的枚举类型,它包含了三个枚举值:RED、GRE…...

开发一套java语言的智能导诊需要什么技术?java+ springboot+ mysql+ IDEA互联网智能3D导诊系统源码
开发一套java语言的智能导诊需要什么技术?java springboot mysql IDEA互联网智能3D导诊系统源码 医院导诊系统是一种基于互联网和3D人体的智能化服务系统,旨在为患者提供精准、便捷的医院就诊咨询服务。该系统整合了医院的各种医疗服务资;智慧…...

C++| STL之string
前言:最近在做LeetCode算法题,C字符串通常都是string作为输入,所以补充一下STL里面string。在介绍的具体使用的时候,会补充char字符串相关的进行对比。 string 创建大小和容量遍历字符串比较插入字符拼接字符串分配内存查找截取分…...