golang之验证器validator
快速安装
使用之前,我们先要获取validator这个库。
# 第一次安装使用如下命令
go get github.com/go-playground/validator/v10
# 项目中引入包
import "github.com/go-playground/validator/v10"
简单示例
安装还是很简单的,下面我先来一个官方样例,看看是怎么使用的,然后展开分析。
package main import (
"fmt"
"net/http" "github.com/gin-gonic/gin"
) type RegisterRequest struct {
Username string `json:"username" binding:"required"`
Nickname string `json:"nickname" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
Age uint8 `json:"age" binding:"gte=1,lte=120"`
} func main() { router := gin.Default() router.POST("register", Register) router.Run(":9999")
} func Register(c *gin.Context) {
var r RegisterRequest
err := c.ShouldBindJSON(&r)
if err != nil {
fmt.Println("register failed")
c.JSON(http.StatusOK, gin.H{"msg": err.Error()})
return
}
//验证 存储操作省略.....
fmt.Println("register success")
c.JSON(http.StatusOK, "successful")
}
- 测试
curl --location --request POST 'http://localhost:9999/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"username": "asong",
"nickname": "golang梦工厂",
"email": "7418.com",
"password": "123",
"age": 140
}'
- 返回结果
{
"msg": "Key: 'RegisterRequest.Email' Error:Field validation for 'Email' failed on the 'email' tag\nKey: 'RegisterRequest.Age' Error:Field validation for 'Age' failed on the 'lte' tag"
}
看这个输出结果,我们可以看到validator的检验生效了,email字段不是一个合法邮箱,age字段超过了最大限制。我们只在结构体中添加tag就解决了这个问题,是不是很方便,下面我们就来学习一下具体使用。
validator库
gin框架是使用validator.v10这个库来进行参数验证的,所以我们先来看看这个库的使用。
先安装这个库:
go get github.com/go-playground/validator/v10
然后先写一个简单的示例:
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Username string `validate:"min=6,max=10"`
Age uint8 `validate:"gte=1,lte=10"`
Sex string `validate:"oneof=female male"`
}
func main() {
validate := validator.New()
user1 := User{Username: "asong", Age: 11, Sex: "null"}
err := validate.Struct(user1)
if err != nil {
fmt.Println(err)
}
user2 := User{Username: "asong111", Age: 8, Sex: "male"}
err = validate.Struct(user2)
if err != nil {
fmt.Println(err)
}
}
我们在结构体定义validator标签的tag,使用validator.New()创建一个验证器,这个验证器可以指定选项、添加自定义约束,然后在调用他的Struct()方法来验证各种结构对象的字段是否符合定义的约束。
上面的例子,我们在User结构体中,有三个字段:
- Name:通过min和max来进行约束,Name的字符串长度为[6,10]之间。
- Age:通过gte和lte对年轻的范围进行约束,age的大小大于1,小于10。
- Sex:通过oneof对值进行约束,只能是所列举的值,oneof列举出性别为男士和女士
所以user1会进行报错,错误信息如下:
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'min' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tag
Key: 'User.Sex' Error:Field validation for 'Sex' failed on the 'oneof' tag
各个字段违反了什么约束,一眼我们便能从错误信息中看出来。看完了简单示例,下面我就来看一看都有哪些tag,我们都可以怎么使用。本文不介绍所有的tag,更多使用方法,请到官方文档自行学习。
字符串约束
excludesall:不包含参数中任意的 UNICODE 字符,例如excludesall=ab;excludesrune:不包含参数表示的 rune 字符,excludesrune=asong;startswith:以参数子串为前缀,例如startswith=hi;endswith:以参数子串为后缀,例如endswith=bye。contains=:包含参数子串,例如contains=email;containsany:包含参数中任意的 UNICODE 字符,例如containsany=ab;containsrune:包含参数表示的 rune 字符,例如`containsrune=asong;excludes:不包含参数子串,例如excludes=email;
范围约束
范围约束的字段类型分为三种:
- 对于数值,我们则可以约束其值
- 对于切片、数组和map,我们则可以约束其长度
- 对于字符串,我们则可以约束其长度
常用tag介绍:
ne:不等于参数值,例如ne=5;gt:大于参数值,例如gt=5;gte:大于等于参数值,例如gte=50;lt:小于参数值,例如lt=50;lte:小于等于参数值,例如lte=50;oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,例如oneof=male female。eq:等于参数值,注意与len不同。对于字符串,eq约束字符串本身的值,而len约束字符串长度。例如eq=10;len:等于参数值,例如len=10;max:小于等于参数值,例如max=10;min:大于等于参数值,例如min=10
Fields约束
eqfield:定义字段间的相等约束,用于约束同一结构体中的字段。例如:eqfield=Passwordeqcsfield:约束统一结构体中字段等于另一个字段(相对),确认密码时可以使用,例如:eqfiel=ConfirmPasswordnefield:用来约束两个字段是否相同,确认两种颜色是否一致时可以使用,例如:nefield=Color1necsfield:约束两个字段是否相同(相对)
常用约束
unique:指定唯一性约束,不同类型处理不同:- 对于map,unique约束没有重复的值
- 对于数组和切片,unique没有重复的值
- 对于元素类型为结构体的碎片,unique约束结构体对象的某个字段不重复,使用
unique=field指定字段名 email:使用email来限制字段必须是邮件形式,直接写eamil即可,无需加任何指定。omitempty:字段未设置,则忽略-:跳过该字段,不检验;|:使用多个约束,只需要满足其中一个,例如rgb|rgba;required:字段必须设置,不能为默认值;
gin中的参数校验
学习了validator,我们也就知道了怎么在gin中使用参数校验了。这些约束是都没有变的,在validator中,我们直接结构体中将约束放到validate tag中,同样道理,在gin中我们只需将约束放到bindingtag中就可以了。是不是很简单。
但是有些时候,并不是所有的参数校验都能满足我们的需求,所以我们可以定义自己的约束。自定义约束支持自定义结构体校验、自定义字段校验等。这里来介绍一下自定义结构体校验。
自定义结构体校验
当涉及到一些复杂的校验规则,这些已有的校验规则就不能满足我们的需求了。例如现在有一个需求,存在db的用户信息中创建时间与更新时间都要大于某一时间。现在我们来写一个简单示例,学习一下怎么对这个参数进行校验。
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
type Info struct {
CreateTime time.Time `form:"create_time" binding:"required,timing" time_format:"2006-01-02"`
UpdateTime time.Time `form:"update_time" binding:"required,timing" time_format:"2006-01-02"`
}
// 自定义验证规则断言
func timing(fl validator.FieldLevel) bool {
if date, ok := fl.Field().Interface().(time.Time); ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}
func main() {
route := gin.Default()
// 注册验证
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
err := v.RegisterValidation("timing", timing)
if err != nil {
fmt.Println("success")
}
}
route.GET("/time", getTime)
route.Run(":8080")
}
func getTime(c *gin.Context) {
var b Info
// 数据模型绑定查询字符串验证
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "time are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
写好了,下面我就来测试验证一下:
$ curl "localhost:8080/time?create_time=2020-10-11&update_time=2020-10-11"
# 结果
{"message":"time are valid!"}%
$ curl "localhost:8080/time?create_time=1997-10-11&update_time=1997-10-11"
# 结果
{"error":"Key: 'Info.CreateTime' Error:Field validation for 'CreateTime' failed on the 'timing' tag\nKey: 'Info.UpdateTime' Error:Field validation for 'UpdateTime' failed on the 'timing' tag"}%
这里我们看到虽然参数验证成功了,但是这里返回的错误显示的也太全了,在项目开发中不可以给前端返回这么详细的信息的,所以我们需要改造一下:
func getTime(c *gin.Context) {
var b Info
// 数据模型绑定查询字符串验证
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "time are valid!"})
} else {
_, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"code": 1000, "msg": "param is error"})
}
}
这里在出现错误时返回固定错误即可。这里你也可以使用一个方法封装一下,对错误进行处理在进行返回,更多使用方法等你发觉哟。
小彩蛋
我们返回错误时都是英文的,当错误很长的时候,对于我这种英语渣渣,就要借助翻译软件了。所以要是能返回的错误直接是中文的就好了。validator库本身是支持国际化的,借助相应的语言包可以实现校验错误提示信息的自动翻译。下面就写一个代码演示一下啦。
package main
import (
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
"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"
chTranslations "github.com/go-playground/validator/v10/translations/zh"
)
var trans ut.Translator
// loca 通常取决于 http 请求头的 'Accept-Language'
func transInit(local string) (err error) {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
zhT := zh.New() //chinese
enT := en.New() //english
uni := ut.New(enT, zhT, enT)
var o bool
trans, o = uni.GetTranslator(local)
if !o {
return fmt.Errorf("uni.GetTranslator(%s) failed", local)
}
//register translate
// 注册翻译器
switch local {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = chTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
return
}
type loginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required,max=16,min=6"`
}
func main() {
if err := transInit("zh"); err != nil {
fmt.Printf("init trans failed, err:%v\n", err)
return
}
router := gin.Default()
router.POST("/user/login", login)
err := router.Run(":8888")
if err != nil {
log.Println("failed")
}
}
func login(c *gin.Context) {
var req loginRequest
if err := c.ShouldBindJSON(&req); err != nil {
// 获取validator.ValidationErrors类型的errors
errs, 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(trans),
})
return
}
//login 操作省略
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
})
}
我这里请求参数中限制密码的长度,来验证一下吧。
curl --location --request POST 'http://localhost:8888/user/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"username": "asong",
"password": "11122222222222222222"
}'
# 返回
{
"msg": {
"loginRequest.Password": "Password长度不能超过16个字符"
}
}




golang之验证器validator的更多相关文章
- vue props 下有验证器 validator 验证数据返回true false后,false给default值
vue props 下有验证器 validator 验证数据返回true false后,false给default值 props: { type: { validator (value) { retu ...
- 设定范围和步长的递增数验证器Validator
1.接口注释 @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Docume ...
- 9、 Struts2验证(声明式验证、自定义验证器)
1. 什么是Struts2 验证器 一个健壮的 web 应用程序必须确保用户输入是合法.有效的. Struts2 的输入验证 基于 XWork Validation Framework 的声明式验证: ...
- Hibernate验证器
第 4 章 Hibernate验证器 http://hibernate.org/validator/documentation/getting-started/#applying-constrain ...
- Flask系列09--Flask中WTForms插件,及自定义验证器
一.概述 django中的forms组件非常的方便,在flask中有WTForms的组件实现的也是类似的功能, 安装这个插件 二.简单使用 文档地址https://wtforms.readthedoc ...
- [Swift]LeetCode591. 标签验证器 | Tag Validator
Given a string representing a code snippet, you need to implement a tag validator to parse the code ...
- Converter(转换器)与Formatter(格式化) ,Validator(验证器)
Converter(转换器)与Formatter(格式化)都可以用于将一种对象类型转换为另一种对象类型.Converter是通用元件,可以在应用程序的任意层中使用,而Fotermatter这是专门为W ...
- PHP验证器类Validator
Particle\Validator是一个小巧优雅的实用的PHP验证类库,提供了一个非常简洁的API.它无需依赖其他组件,提供友好的文档,并且有利于扩展. 安装 composer require pa ...
- validator js验证器
转自:https://github.com/jaywcjlove/validator.js 轻量级的JavaScript表单验证,字符串验证.没有依赖,支持UMD 导入js库 <script t ...
- golang 如何验证struct字段的数据格式
本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/125 假设我们有如下结构体: type User struct ...
随机推荐
- 学习问题记录:RocketMQ集成到SpringBoot后,消费者无法自动进行消息消费。
情况说明 在SpringBoot中集成了RocketMQ,实践过程中,通过RocketMQ DashBoard观察,生产者可以正常将进行消息提交:通过日志及DashBoard观察,消费者成功在Rock ...
- JavaScript习题之判断题
1. JavaScript是Java语言的脚本形式.( ) 2. JavaScript中的方法名不区分大小写.( ) 3. JavaScript语句结束时的分号可以省略.( ) 4. 通过外链式引入J ...
- 借助 Flutter 顺畅地开发多平台应用
Flutter 已于近期发布了 Flutter 2,Flutter 和 Dart 的产品总监 Tim Sneath 在 2021 年三月上旬举办的 Flutter Engage 活动中表示,Flutt ...
- 配置windows update失败还原更改
配置windows update失败还原更改_解决方案 解决方法: 方法1: 重启,按F8,选择最后一次正常启动. 如果还是需要等待.可采用方法2: 方法2: 重启,按F8,选 ...
- 《Neo4j 图数据库扩展指南:APOC和ALGO》
https://detail.tmall.com/item.htm?spm=a2e2i.11532906.0.d2960ced2.f27a6abbrEMtHp&id=622478213458 ...
- .net 调用 nsfwjs 进行视频鉴别
1. npm 安装 nsfwjs npm install express --save npm install multer --save npm install jpeg-js --save npm ...
- CMake 属性之目录属性
[写在前面] CMake 的目录属性是指在特定目录(及其子目录)范围内有效的设置. 这些属性不同于全局变量或目标(Target)属性,它们提供了一种机制,允许开发者为项目中的不同部分定义不同的构建行为 ...
- iOS解决嵌套在ScrollView中的TableView滑动手势冲突问题
在项目开发中用到了这种需求,顶部两个选项卡,底部列表有侧滑手势,布局是就不说了,遇到的问题是左滑的时候添加滑动手势效果.一直纠结了很久,后来想到可能是手势冲突的问题. 解决办法开启多手势代理即可,我是 ...
- C#查漏补缺----对象内存结构与布局
环境变量 .Net Core 8.0 Windows 11 64位 内存布局 引用类型 在.NET中,数据会按照类型分为不同的对象,对于引用类型的实例,由一个对象标头(Object Header)和方 ...
- kotlin更多语言结构——>作用域函数
作用域函数 Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块.当对一个对象调用这样的函数 并提供一个 lambda 表达式时,它会形成一个临时作用域.在此作用域中,可以访问 ...