validator库的使用详解

@TOC

基本使用

前言

在做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唯一

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/131572.html

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

相关文章

串的基本操作(数据结构)

串的基本操作 #include <stdlib.h> #include <iostream> #include <stdio.h> #define MaxSize 255typedef struct{char ch[MaxSize];int length; }SString;//初始化 SString InitStr(SString &S){S.length0;return S; } //为了方便计算&#xff0c;串的…

ctfshow-web12(glob绕过)

打开链接&#xff0c;在网页源码里找到提示 要求以get请求方式给cmd传入参数 尝试直接调用系统命令&#xff0c;没有回显&#xff0c;可能被过滤了 测试phpinfo&#xff0c;回显成功&#xff0c;确实存在了代码执行 接下来我们尝试读取一下它存在的文件&#xff0c;这里主要介…

Factory-Method

Factory-Method 动机 在软件系统中&#xff0c;经常面临着创建对象的工作&#xff1b;由于需求的变化&#xff0c;需要创建的对象的具体类型经常变化。如何应对这种变化&#xff1f;如何绕过常规的对象创建方法(new)&#xff0c;提供一种“封装机制”来避免客户程序和这种“具…

Go 并发编程

并发编程 1.1 并发与并⾏ 并⾏与并发是两个不同的概念&#xff0c;普通解释&#xff1a; 并发&#xff1a;交替做不同事情的能⼒并⾏&#xff1a;同时做不同事情的能⼒ 如果站在程序员的⻆度去解释是这样的&#xff1a; 并发&#xff1a;不同的代码块交替执⾏并⾏&#xf…

蓝桥杯每日一题2023.10.11

子串分值和 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 以下为50分方法&#xff08;暴力枚举&#xff09; 第一层循环枚举其长度&#xff0c;第二层循环枚举其左端点&#xff0c;k代表右端点&#xff0c;&#xff08;将每一种子串一一枚举出来&#xff09;算出从左端点到右…

新手教程!制作电子期刊的必备网站

随着数字时代的到来&#xff0c;电子期刊已经成为一种重要的阅读方式。无论是为了学习、工作还是娱乐&#xff0c;电子期刊都以其方便、快捷、多样化的特点&#xff0c;越来越受到人们的欢迎。 对于想要制作电子期刊的新手来说&#xff0c;首先需要找到一些专业的工具来制作电子…

JavaScript和Java的区别是什么?

目录 一、不同的公司开发的不同产品。 二、使用的方向不同 三、对象不同 四、HTML文档中采用的标识不同 五、语言类型不同 六、在浏览器中所执行的方式不同 七、代码格式不同 八、所采取的变量是不同 九、联编方式不一样 十、对图文操作方式不一样 很多初学者看到Jav…

Flink session集群运维

1、集群job manager挂了 kubectl describe pod session-deployment-only-84b8d674c7-ckl9w -n flink kubectl get pod -n flink -owide kubectl describe pod session-deployment-only-84b8d674c7-ms758 -n flink 两个job manager都挂了 准备重新部署集群 删除操作(删除fli…

深度学习基础知识数据 数据预处理transforms流程讲解

深度学习基础知识数据 数据预处理transforms流程讲解 1、数据预处理2、使用节点2、transform.RandomResizedCrop 随机尺寸裁剪缩放3、水平翻转 与 垂直翻转4、ColorJitter变换5、ToTensor6、Normalization 归一化7、transforms.Compose8、重写transforms1、分类任务2、目标检测…

Elasticsearch基础篇(四):Elasticsearch的基础介绍与索引设置

Elasticsearch的基础介绍与索引设置 一、Elasticsearch概述Elasticsearch简介什么是全文检索引擎Elasticsearch 应用案例 二、索引和文档的概念1. 索引&#xff08;Index&#xff09;2. 文档&#xff08;Document&#xff09; 三、倒排索引&#xff08;Inverted Index&#xff…

VS2017+QT+PCL环境配置

1、前置知识 C++ Qt VTK/OPenGL喧杂C 2、环境搭建 1、visual studio 2017安装,从官网上下载对应安装程序。选择C++安装即可 2、Qt安装若是没有账号和密码,选择dont have qt accout? Sign up 根据自己的项目需要选择安装那些组件 2、PCL安装 下载地址:https://github.co…

广州华锐互动:VR互动教学平台如何赋能职业院校?

随着科技的发展&#xff0c;我们的教育方式也在不断进步。其中&#xff0c;虚拟现实&#xff08;VR&#xff09;技术的出现为我们提供了一种全新的教学方式。特别是在职业学校中&#xff0c;VR互动教学平台已经成为一种重要的教学工具。 VR互动教学平台是一种利用虚拟现实技术创…