Golang使用validator进行数据校验及自定义翻译器
Golang使用validator进行数据校验及自定义翻译器
包下载:go get github.com/go-playground/validator/v10
一、概述
在接口开发经常会遇到一个问题是后端需要写大量的繁琐代码进行数据校验,所以就想着有没有像前端校验一样写规则进行匹配校验,然后就发现了validator包,一个比较强大的校验工具包下面是一些学习总结,详细内容可以查看validator
二、操作符说明
标记 | 标记说明 |
---|---|
, | 多操作符分割 |
| | 或操作 |
- | 跳过字段验证 |
三、常用标记说明
标记 | 标记说明 | 例 |
---|---|---|
required | 必填 | Field或Struct validate:"required" |
omitempty | 空时忽略 | Field或Struct validate:"omitempty" |
len | 长度 | Field validate:"len=0" |
eq | 等于 | Field validate:"eq=0" |
gt | 大于 | Field validate:"gt=0" |
gte | 大于等于 | Field validate:"gte=0" |
lt | 小于 | Field validate:"lt=0" |
lte | 小于等于 | Field validate:"lte=0" |
eqfield | 同一结构体字段相等 | Field validate:"eqfield=Field2" |
nefield | 同一结构体字段不相等 | Field validate:"nefield=Field2" |
gtfield | 大于同一结构体字段 | Field validate:"gtfield=Field2" |
gtefield | 大于等于同一结构体字段 | Field validate:"gtefield=Field2" |
ltfield | 小于同一结构体字段 | Field validate:"ltfield=Field2" |
ltefield | 小于等于同一结构体字段 | Field validate:"ltefield=Field2" |
eqcsfield | 跨不同结构体字段相等 | Struct1.Field validate:"eqcsfield=Struct2.Field2" |
necsfield | 跨不同结构体字段不相等 | Struct1.Field validate:"necsfield=Struct2.Field2" |
gtcsfield | 大于跨不同结构体字段 | Struct1.Field validate:"gtcsfield=Struct2.Field2" |
gtecsfield | 大于等于跨不同结构体字段 | Struct1.Field validate:"gtecsfield=Struct2.Field2" |
ltcsfield | 小于跨不同结构体字段 | Struct1.Field validate:"ltcsfield=Struct2.Field2" |
ltecsfield | 小于等于跨不同结构体字段 | Struct1.Field validate:"ltecsfield=Struct2.Field2" |
min | 最大值 | Field validate:"min=1" |
max | 最小值 | Field validate:"max=2" |
structonly | 仅验证结构体,不验证任何结构体字段 | Struct validate:"structonly" |
nostructlevel | 不运行任何结构级别的验证 | Struct validate:"nostructlevel" |
dive | 向下延伸验证,多层向下需要多个dive标记 | [][]string validate:"gt=0,dive,len=1,dive,required" |
dive Keys & EndKeys | 与dive同时使用,用于对map对象的键的和值的验证,keys为键,endkeys为值 | map[string]string validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,required" |
required_with | 其他字段其中一个不为空且当前字段不为空 | Field validate:"required_with=Field1 Field2" |
required_with_all | 其他所有字段不为空且当前字段不为空 | Field validate:"required_with_all=Field1 Field2" |
required_without | 其他字段其中一个为空且当前字段不为空 | Field `validate:"required_without=Field1 Field2" |
required_without_all | 其他所有字段为空且当前字段不为空 | Field validate:"required_without_all=Field1 Field2" |
isdefault | 是默认值 | Field validate:"isdefault=0" |
oneof | 其中之一 | Field validate:"oneof=5 7 9" |
containsfield | 字段包含另一个字段 | Field validate:"containsfield=Field2" |
excludesfield | 字段不包含另一个字段 | Field validate:"excludesfield=Field2" |
unique | 是否唯一,通常用于切片或结构体 | Field validate:"unique" |
alphanum | 字符串值是否只包含 ASCII 字母数字字符 | Field validate:"alphanum" |
alphaunicode | 字符串值是否只包含 unicode 字符 | Field validate:"alphaunicode" |
alphanumunicode | 字符串值是否只包含 unicode 字母数字字符 | Field validate:"alphanumunicode" |
numeric | 字符串值是否包含基本的数值 | Field validate:"numeric" |
hexadecimal | 字符串值是否包含有效的十六进制 | Field validate:"hexadecimal" |
hexcolor | 字符串值是否包含有效的十六进制颜色 | Field validate:"hexcolor" |
lowercase | 符串值是否只包含小写字符 | Field validate:"lowercase" |
uppercase | 符串值是否只包含大写字符 | Field validate:"uppercase" |
字符串值包含一个有效的电子邮件 | Field validate:"email" |
|
json | 字符串值是否为有效的 JSON | Field validate:"json" |
file | 符串值是否包含有效的文件路径,以及该文件是否存在于计算机上 | Field validate:"file" |
url | 符串值是否包含有效的 url | Field validate:"url" |
uri | 符串值是否包含有效的 uri | Field validate:"uri" |
base64 | 字符串值是否包含有效的 base64值 | Field validate:"base64" |
contains | 字符串值包含子字符串值 | Field validate:"contains=@" |
containsany | 字符串值包含子字符串值中的任何字符 | Field validate:"containsany=abc" |
containsrune | 字符串值包含提供的特殊符号值 | Field validate:"containsrune=☢" |
excludes | 字符串值不包含子字符串值 | Field validate:"excludes=@" |
excludesall | 字符串值不包含任何子字符串值 | Field validate:"excludesall=abc" |
excludesrune | 字符串值不包含提供的特殊符号值 | Field validate:"containsrune=☢" |
startswith | 字符串以提供的字符串值开始 | Field validate:"startswith=abc" |
endswith | 字符串以提供的字符串值结束 | Field validate:"endswith=abc" |
ip | 字符串值是否包含有效的 IP 地址 | Field validate:"ip" |
ipv4 | 字符串值是否包含有效的 ipv4地址 | Field validate:"ipv4" |
datetime | 字符串值是否包含有效的 日期 | Field validate:"datetime" |
四、标记使用注意
1、当搜索条件与特殊标记冲突时,如:逗号(,),或操作(|),中横线(-)等则需要使用 UTF-8十六进制表示形式
例:
type Test struct {
Field1 string `validate:"excludesall=|"` // 错误
Field2 string `validate:"excludesall=0x7C"` // 正确.
}
2、可通过validationErrors := errs.(validator.ValidationErrors)获取错误对象自定义返回响应错误
3、自定义校验结果翻译
// 初始化翻译器
func validateInit() {
zh_ch := zh.New()
uni := ut.New(zh_ch) // 万能翻译器,保存所有的语言环境和翻译数据
Trans, _ = uni.GetTranslator("zh") // 翻译器
Validate = validator.New()
_ = zh_translations.RegisterDefaultTranslations(Validate, Trans)
// 添加额外翻译
_ = Validate.RegisterTranslation("required_without", Trans, func(ut ut.Translator) error {
return ut.Add("required_without", "{0} 为必填字段!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required_without", fe.Field())
return t
})
}
五、使用示例
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
// 实例化验证对象
var validate = validator.New()
func main() {
// 结构体验证
type Inner struct {
String string `validate:"contains=111"`
}
inner := &Inner{String: "11@"}
errs := validate.Struct(inner)
if errs != nil {
fmt.Println(errs.Error())
}
// 变量验证
m := map[string]string{"": "", "val3": "val3"}
errs = validate.Var(m, "required,dive,keys,required,endkeys,required")
if errs != nil {
fmt.Println(errs.Error())
}
}
六、gin框架中使用验证翻译器
1. 定义错误翻译器
//validator.go
// 定义一个全局翻译器
var trans ut.Translator
// InitTrans 初始化翻译器
func InitTrans(locale string) (err error) {
//修改gin框架中的Validator属性,实现自定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// 注册一个获取json tag的自定义方法
v.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(zhT, zhT) 也是可以的
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)
}
// 添加额外翻译
_ = v.RegisterTranslation("required_with", trans, func(ut ut.Translator) error {
return ut.Add("required_with", "{0} 为必填字段!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required_with", fe.Field())
return t
})
_ = v.RegisterTranslation("required_without", trans, func(ut ut.Translator) error {
return ut.Add("required_without", "{0} 为必填字段!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required_without", fe.Field())
return t
})
_ = v.RegisterTranslation("required_without_all", trans, func(ut ut.Translator) error {
return ut.Add("required_without_all", "{0} 为必填字段!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required_without_all", fe.Field())
return t
})
// 注册翻译器
switch locale {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
return
}
func addValueToMap(fields map[string]string) map[string]interface{} {
res := make(map[string]interface{})
for field, err := range fields {
fieldArr := strings.SplitN(field, ".", 2)
if len(fieldArr) > 1 {
NewFields := map[string]string{fieldArr[1]: err}
returnMap := addValueToMap(NewFields)
if res[fieldArr[0]] != nil {
for k, v := range returnMap {
res[fieldArr[0]].(map[string]interface{})[k] = v
}
} else {
res[fieldArr[0]] = returnMap
}
continue
} else {
res[field] = err
continue
}
}
return res
}
// 去掉结构体名称前缀
func removeTopStruct(fields map[string]string) map[string]interface{} {
lowerMap := map[string]string{}
for field, err := range fields {
fieldArr := strings.SplitN(field, ".", 2)
lowerMap[fieldArr[1]] = err
}
res := addValueToMap(lowerMap)
return res
}
//handler中调用的错误翻译方法
func ErrResp(err error) *gin.H {
errs, ok := err.(validator.ValidationErrors)
fmt.Println(reflect.TypeOf(err))
if !ok {
return &gin.H{
"errCode": -1,
"errMsg": err.Error(), // 翻译校验错误提示
}
}
return &gin.H{
"errCode": -1,
"errMsg": removeTopStruct(errs.Translate(trans)), // 翻译校验错误提示
}
}
2.使用
//router.go
//初始化翻译器,这部分可放在main.go或router.go中
if err := util.InitTrans("zh"); err != nil {
log.Fatalf("init trans failed, err:%v\n", err)
return
}
//handler.go
//翻译错误
func (h handler) Login(c *gin.Context) {
req := models.LoginReq{}
err := c.ShouldBindWith(&req, binding.JSON)
if err != nil {
c.JSON(http.StatusBadRequest, util.ErrResp(err)) //这里调用翻译
return
}
}
Golang使用validator进行数据校验及自定义翻译器的更多相关文章
- .net 使用validator做数据校验
概述 在把用户输入的数据存储到数据库之前一般都要对数据做服务端校验,于是想到了.net自带的数据校验框架validator.本文对validator的使用方法进行介绍,并分析下校验的的原理. 使用va ...
- 数据校验validator 与 DWZ
在做系统时经常会用到数据校验,数据校验可以自己写,也可以用现在成的,现在记录下两种类库使用方法, <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 ...
- 数据校验DWZ与validator
在做系统时经常会用到数据校验,数据校验可以自己写,也可以用现在成的,现在记录下两种类库使用方法, validato <!DOCTYPE HTML PUBLIC "-//W3C//DTD ...
- @Valid 数据校验 + 自定义全局异常信息
关于javax.validation.Validator校验的使用 对于要校验的实体类:其需要校验的字段上需要添加注解 实际例子 使用:首先要拿到 validator的子类 Validator val ...
- JSR303的数据校验-Hibernate Validator方式实现
1.什么是JSR303? JSR303是java为bean数据合法性校验所提供的一个标准规范,叫做Bean Validation. Bean Validation是一个运行时的数据校验框架,在验证之后 ...
- 使用Hibernate Validator来帮你做数据校验
数据校验是贯穿所有应用程序层(从表示层到持久层)的常见任务.通常在每个层中实现相同的验证逻辑,这是耗时且容易出错的.这里我们可以使用Hibernate Validator来帮助我处理这项任务.对此,H ...
- 用spring的@Validated注解和org.hibernate.validator.constraints.*的一些注解在后台完成数据校验
这个demo主要是让spring的@Validated注解和hibernate支持JSR数据校验的一些注解结合起来,完成数据校验.这个demo用的是springboot. 首先domain对象Foo的 ...
- SpringBoot 2 快速整合 | Hibernate Validator 数据校验
概述 在开发RESTFull API 和普通的表单提交都需要对用户提交的数据进行校验,例如:用户姓名不能为空,年龄必须大于0 等等.这里我们主要说的是后台的校验,在 SpringBoot 中我们可以通 ...
- Struts 2 数据校验要用到的类和两种校验方式以及一些校验问题的解决
通过继承ActionSupport类来完成Action开发,ActionSupport类不仅对Action接口进行简单实现, 同时增加了验证.本地化等支持 .真实开发中自定义Action都需要继承该类 ...
随机推荐
- Beta实际开发与初始计划的比较
零.说明 本篇博客为Beta阶段开始十天后,实际开发工作与初始计划的比较 截止至本篇博客发布为止,团队所有成员已完成计网考试,将在本周日进行充分的接口测试 一.比较 1.与初始计划对比 初始计划 实际 ...
- 嵌入式STM32的GPIO口工作模式的介绍
一.输入模式 1. 浮空输入 浮空输入模式下,上拉和下拉两个开关断开,高或低电平通过施密特触发器到达输入数据寄存器,CPU可以通过读取输入数据寄存器从而读取到外部输入的高低电平值. 2. 输入上拉模式 ...
- 设计模式 - Bridge 桥模式
Bridge桥模式也属于"的单一职责"模式中的典型模式.问题描述:我们绘制图形时,图形可以有不同形状以及不同颜色,比如圆形可以是红的,绿的,方形可以是红的绿的,如果用代码来描绘这些 ...
- 全志Linux Tina编译demoOmxVdec错误
测试裸流 Making install in demoOmxVdec make[6]: Entering directory '/home/liuxueneng/WorkCode/Homlet-Tin ...
- 数据流中的中位数 牛客网 剑指Offer
数据流中的中位数 牛客网 剑指Offer 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就 ...
- Oracle日志 归档模式管理
一.查看当前归档模式 archive log list; 二.归档/不归档切换 shutdown immediate; -- 或shutdown normal startup mount; alter ...
- Can't open PID file /run/zabbix/zabbix_agentd.pid
神奇的事情,重启主机(reboot)后查看状态是正常的没有报错,重启zabbix-agent后,报无法打开zabbix_agentd.pid,zabbix-web正常监控 1.首先,/etc/zabb ...
- 关于Python中用户输入字符串(与变量名相同)无法作为变量名引用的理解以及解决方案
在用户登录账号时,我需要在字典中查找是否存在其账号名称以及密码是否正确. 所以,我想将用户输入的账号赋值给变量,在字典中查找是否有此指值. 代码如下: 1 Ya = {'姓名': 'Ya', 'pas ...
- 菜鸡的Java笔记 java数据库编程(JDBC)
java数据库编程(JDBC) 介绍 JDBC 的基本功能 content (内容) 现在几乎所有的项目开发过程之中都不可能离开数据库,所以在java ...
- python中整除后结果也是小数
有人这么回答,这显然不对 先看个例子: '//'明明是整除,为什么结果不是整数,而会出现小数? 首先,关于除法有三种概念:传统除法.精确除法和地板除 #1.传统除法:整数相除结果是整数,浮点数相除结果 ...