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

validator库的使用详解

目录

    • 基本使用
      • 前言
      • 请求模型的定义
      • 编写接口及测试
    • 翻译校验错误提示信息
    • 自定义错误提示信息的字段名
    • 自定义校验规则
    • 常见的参数校验字段

基本使用

前言

在做API开发时,需要对请求参数的校验,防止用户的恶意请求。例如日期格式,用户年龄,性别等必须是正常的值,不能随意设置。以前会使用大量的if判断参数的值是否符合规范,现在可以使用validator库来进行参数校验。我们只需要在结构体的Tag中添加 validator 标签就可以实现参数校验。同时Gin框架当前内部也集成了validator.v10这个库,在Gin框架中只要在结构体的Tag中添加 binding 标签就可以实现参数校验。使用binding标签和validator标签都是可以的,但是在Gin框架中我们一般会使用binding标签来实现参数校验。

请求模型的定义

//请求的参数必须存在并且由数字或字母组成
type UserRequest struct {Username string `json:"username" form:"username" binding:"required,alphanum"`Password string `json:"password" form:"password" binding:"required,alphanum"`
}

编写接口及测试

// ModifyUser 用来修改用户的密码
func ModifyUser(c *gin.Context) {var user UserRequestif err := c.BindQuery(&user); err != nil {c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})return}//查询是否存在该用户result := service.SelectUserIfExist(user.Username)if result == 1 { //该用户存在userInfo := service.ChangeUserSecret(user.Username, user.Password)c.JSON(http.StatusOK, gin.H{"msg": userInfo,})} else {c.JSON(http.StatusOK, gin.H{"msg": "用户不存在",})}
}

现在数据库中有一个用户。

mysql> select * from user;
+---------+----------+----------+
| user_id | username | password |
+---------+----------+----------+
|       4 | abc123   | abc123   |
+---------+----------+----------+

我们在修改密码时,将密码设置为空或者改成 xyz@456 看看参数校验能不能通过。

image-20231011173623783 image-20231011173823585

可以看到将密码设置为空或者改成 xyz@456 会曝出不同的错误。下面使用满足校验规则的密码进行测试:

image-20231011174027471

可以看到密码已经被成功修改。

翻译校验错误提示信息

当参数校验错误的时候会返回错误信息,可以看到上面的错误信息都是英文,有时候看英文确实会不太舒服,所以也没有什么办法可以把错误信息翻译成中文?答案是有的,validator 库本身是支持国际化的,借助相应的语言包可以实现校验错误提示信息的自动翻译。下面的示例代码演示了如何将错误提示信息翻译成中文。可以新建一个目录utils,目录下面新建一个translate.go文件。文件内容如下:

package utilsimport ("fmt""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/en""github.com/go-playground/locales/zh"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"enTranslations "github.com/go-playground/validator/v10/translations/en"zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)// Trans 一个全局翻译器
var Trans ut.Translatorfunc Translate(locale string) (err error) {// 修改gin框架中的Validator引擎属性,实现自定制if value, ok := binding.Validator.Engine().(*validator.Validate); ok {zhT := zh.New() // 中文翻译器enT := en.New() // 英文翻译器// 第一个参数是备用(fallback)的语言环境// 后面的参数是支持的语言环境(支持多个)uni := ut.New(enT, zhT, enT)// locale 通常取决于 http 请求头的 'Accept-Language'var ok bool// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找Trans, ok = uni.GetTranslator(locale)if !ok {return fmt.Errorf("uni.GetTranslator(%s) failed", locale)}// 注册翻译器switch locale {case "en":err = enTranslations.RegisterDefaultTranslations(value, Trans)case "zh":err = zhTranslations.RegisterDefaultTranslations(value, Trans)default:err = enTranslations.RegisterDefaultTranslations(value, Trans)}return}return
}

然后需要在main函数中初始化 Translate 这个函数。

func main() {if err := utils.Translate("zh"); err != nil {fmt.Printf("init trans failed, err:%v\n", err)return}
}

接下来就能使用这个翻译器翻译英文信息。

// ModifyUser 用来修改用户的密码
func ModifyUser(c *gin.Context) {var user UserRequestif err := c.ShouldBindQuery(&user); err != nil {// 获取validator.ValidationErrors类型的errorserrs, ok := err.(validator.ValidationErrors)if !ok {// 非validator.ValidationErrors类型错误直接返回c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})return}// validator.ValidationErrors类型错误则进行翻译c.JSON(http.StatusOK, gin.H{"msg": errs.Translate(utils.Trans),})return}//查询是否存在该用户result := service.SelectUserIfExist(user.Username)if result == 1 { //该用户存在userInfo := service.ChangeUserSecret(user.Username, user.Password)c.JSON(http.StatusOK, gin.H{"msg": userInfo,})} else {c.JSON(http.StatusOK, gin.H{"msg": "用户不存在",})}
}

测试结果如下:

image-20231011192954175 image-20231011192918100

自定义错误提示信息的字段名

上面的错误提示看起来是可以了,但是还是差点意思,首先是错误提示中的字段并不是请求中使用的字段,例如:Password 是我们后端定义的结构体中的字段名,而请求中使用的是小写的 password 字段。如何让错误提示中的字段使用自定义的名称,例如json tag指定的值呢?

其实只需要在初始化翻译器的时候像下面一样添加一个获取json tag的自定义方法即可。

func Translate(locale string) (err error) {// 修改gin框架中的Validator引擎属性,实现自定制if value, ok := binding.Validator.Engine().(*validator.Validate); ok {// 注册一个获取json tag的自定义方法value.RegisterTagNameFunc(func(fld reflect.StructField) string {name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]if name == "-" {return ""}return name})zhT := zh.New() // 中文翻译器enT := en.New() // 英文翻译器// 第一个参数是备用(fallback)的语言环境// 后面的参数是支持的语言环境(支持多个)uni := ut.New(enT, zhT, enT)······}return
}

现在错误信息中的password就变成了小写了。测试结果如下:

image-20231011194814448

但是还是有点瑕疵,那就是最终的错误提示信息中心还是有我们后端定义的结构体名称(UserRequest),这个名称其实是不需要随错误提示返回给前端的,前端并不需要这个值。我们需要想办法把它去掉。其实可以定义一个去掉结构体名称前缀的自定义方法:

func removeTopStruct(fields map[string]string) map[string]string {res := map[string]string{}for field, err := range fields {res[field[strings.Index(field, ".")+1:]] = err}return res
}

我们在代码中使用上述函数将翻译后的 errors 做一下处理即可:

if err := c.ShouldBindQuery(&user); err != nil {// 获取validator.ValidationErrors类型的errorserrs, ok := err.(validator.ValidationErrors)if !ok {// 非validator.ValidationErrors类型错误直接返回c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})return}// validator.ValidationErrors类型错误则进行翻译// 并使用removeTopStruct函数去除字段名中的结构体名称标识c.JSON(http.StatusOK, gin.H{"msg": removeTopStruct(errs.Translate(utils.Trans)),})return}

测试结果如下:

image-20231011195855386

自定义校验规则

先在又有一个需求,那就是要求密码必须是由数字、字母或下划线组成,且第一个字符不能是下划线,那么这个需求又该如何实现呢?

func CustomFunc(fl validator.FieldLevel) bool {// 正则表达式来匹配字母、数字和下划线,且第一个字符不能是下划线re := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_]*$`)return re.MatchString(fl.Field().String())
}// tag参数就是我们自己自定义的校验规则的名字
func Custom(tag string) (err error) {if value, ok := binding.Validator.Engine().(*validator.Validate); ok {// 在校验器注册自定义的校验方法if err := value.RegisterValidation(tag, CustomFunc); err != nil {return err}}return err
}

接下来只需要在main函数或者一些初始化函数当中注册这个Custom函数就可以了。需要注意的是,要添加上binding的字段校验规则,比如:

// Password的校验规则就是我们自定义的校验规则:password
type UserRequest struct {Username string `json:"username" form:"username" binding:"required,alphanum"`Password string `json:"password" form:"password" binding:"required,password"`
}

测试结果如下:

image-20231011204702960 image-20231011204618021

可以看到 abc_123 满足校验规则,而 _abc123 不满足校验规则。

常见的参数校验字段

下面是一些常见的校验规则:

Tag描述
eqfield一个字段等于另一个字段
alpha仅限字母
alphanum仅限字母数字
excludes排除
jsonJSON
jwtJSON Web Token (JWT)
emailE-mail 字符串
html_encodedHTML编码
eq等于
gt大于
gte大于或等于
lt小于
lte小于或等于
ne不等于
len长度
max最大
min最小
required必需的
unique唯一

相关文章:

validator库的使用详解

目录 基本使用前言请求模型的定义编写接口及测试 翻译校验错误提示信息自定义错误提示信息的字段名自定义校验规则常见的参数校验字段 基本使用 前言 在做API开发时,需要对请求参数的校验,防止用户的恶意请求。例如日期格式,用户年龄&#x…...

ADS版图中连接提示线设置

ADS版图连接提示线设置 简述solve 简述 在ADS版中连接提示线设置,如下图1所示,有点类似于AD中“金线”,提示同一网络的焊盘,但在ads中,是产生了同一层的wire,证据如图2所示。如果没有设置的话,…...

【MySQL】内置函数——数学函数+其他函数

文章目录 一. 数字函数二. 其他函数 一. 数字函数 函数名称描述abs()绝对值函数bin()十进制转换二进制hex()转换成十六进制conv(number,from_base,to_base)将number从from_base转换成to_base进制ceiling()向上取整floor()向下取整format(number,decimal_places)格式化&#xf…...

Ubuntu 23.10 Beta 镜像开放下载

导读Canonical放出了 Ubuntu 23.10 Beta 镜像,此外 Edubuntu、Kubuntu、Lubuntu、Ubuntu Budgie、Ubuntu Cinnamon、Ubuntu Kylin、Ubuntu MATE、Ubuntu Studio、Ubuntu Unity 和 Xubuntu 等风味版本也同步放出镜像。 近日消息,Canonical 放出了 Ubuntu …...

mybatispagehelp嵌套分页处理

1.定义嵌套vo类 /*** 用户中心,我的订单列表VO*/ public class MyOrdersVO {private String orderId;private Date createdTime;private Integer payMethod;private Integer realPayAmount;private Integer postAmount...

增速波动!W「下」AR「上」!HUD前装供应商比拼硬核能力

作为汽车智能座舱人机交互的新配置之一,HUD(抬头显示)市场在经历一波快速增长周期后,进入调整阶段。 本周,高工智能汽车研究院发布数据显示,2023年8月,中国市场(不含进出口&#xff…...

XXE漏洞复现实操

文章目录 一、漏洞原理二、验证payload三、没有回显时的验证四、漏洞特征五、读取文件六、Base64加密读取七、端口检测八、使用php检测端口九、dtd外部实体读取文件十、Xxe漏洞防御 一、漏洞原理 (1)XXE漏洞全称XML External Entity Injection,即xmI外部实体注入漏…...

github创建个人网页登录后404无法显示的问题

1.首先必须要有内容,默认是会找index.html文件,找不到该文件会找readme.md文件,也就是说最简单的方法是,创建了与用户名同名的repository后username.github.io后,添加一个readme.md文件,得在readme里打点字…...

MySQL——源码安装教程(初版)

MySQL 一、MySQL的安装1、RPM2、二进制3、源码 二、源码安装方式三、安装过程1、上传源码包2、解压当前文件并安装更新依赖3、对MySQL进行编译安装 一、MySQL的安装 首先这里我来介绍下MySQL的几种安装方式: 一共三种,RPM安装包、二进制包安装以及源码安…...

1.1.C++项目:仿muduo库实现并发服务器之any类的设计

文章目录 一、思想二、框架三、代码 一、思想 每⼀个Connection对连接进行管理,最终都不可避免需要涉及到应用层协议的处理,因此在Connection中需要设置协议处理的上下文来控制处理节奏。但是应用层协议千千万,为了降低耦度,这个…...

linux项目启动脚本start.sh和stop.sh停止脚本

start脚本示例 MAIN_CLASSCLASSPATH.;JVM_PARMS""export APP_HOME$(dirname $(pwd))export APP_APPS$APP_HOME/appsexport APP_RESOURCES$APP_HOME/confexport APP_LIB$APP_HOME/libexport APP_LOG$APP_HOME/logs;export APP_NAME$(basename $APP_HOME)function mak…...

Avalonia常用小控件Svg

1.项目下载地址:https://gitee.com/confusedkitten/avalonia-demo 2.UI库Semi.Avalonia,项目地址 https://github.com/irihitech/Semi.Avalonia 3.SVG库,Avalonia.Svg.Skia,项目地址 https://github.com/wieslawsoltes/Svg.Ski…...

设计模式-行为型模式

文章目录 一、模板方法模式二、策略模式三、命令模式四、责任链模式五、状态模式六、观察者模式七、中介者模式八、迭代器模式九、访问者模式十、备忘录模式十一、解释器模式 一、模板方法模式 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中&#xff0…...

【EventLoop】问题一次搞定

📍 JS的事件循环机制恐怕是大多数前端开发者头顶上的一座大山之一,最近通过拜读两篇文档,对eventloop进行了深刻的理解;通过这篇文档对要点进行总结; article1: 波神的这篇eventLoop文章适合反复重温&…...

Unity中Shader光照模型Phong

文章目录 前言一、Phong光照模型二、图示解释Phone光照模型1、由图可得,R 可以由 -L 加上 P 得出2、P等于2*M3、因为 N 和 L 均为单位向量,所以 M 的模可以由 N 和 L得出4、得到M的模后,乘以 单位向量N,得到M5、最后得出 P 和 R 前…...

消息队列缓存,以蓝牙消息服务为例

前言 消息队列缓存,支持阻塞、非阻塞模式;支持协议、非协议模式 可自定义消息结构体数据内容 使用者只需设置一些宏定义、调用相应接口即可 这里我用蓝牙消息服务举例 有纰漏请指出,转载请说明。 学习交流请发邮件 1280253714qq.com 原…...

MSF派生给另外MSF,meterpreter派生给另外meterpreter,Metasploit

首先是通过ms17_010永恒之蓝拿下shell,192.168.50.146为受害者靶机,192.168.50.130为kali的ip set autorunscript post/windows/manage/migrate nameservices.exe set payload windows/x64/meterpreter/reverse_tcp set lport 5577 set lhost 192.168.50.130 use exploit/windo…...

【LeetCode】1.两数之和

目录 1 问题2 答案2.1 枚举法 (自己写的)2.2 哈希表 3 问题 1 问题 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应…...

3. Windows下C++/MFC调用hiredis库操作redis示例

一、头文件目录 将之前下载和编译好的Redis目录拷贝到新建好的工程目录下面,再点击测试工程的右键/属性,点击C/常规,附加包含目录添加以下路径,注意如果原先有多个路径,在末尾处添加分号后再粘贴: 点击C/常…...

200、使用默认 Exchange 实现 P2P 消息 之 消息生产者(发送消息) 和 消息消费者(消费消息)

RabbitMQ 工作机制图: Connection: 代表客户端(包括消息生产者和消费者)与RabbitMQ之间的连接。 Channel: 连接内部的Channel。channel:通道 Exchange: 充当消息交换机的组件。 Queue&#xff…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

Java 加密常用的各种算法及其选择

在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...

DiscuzX3.5发帖json api

参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...