validate第三方验证库
介绍:
在我们的平常业务中难免会遇到参数验证的情况,这就不免需要我们手动的为每组参数写一段代码:是否满足某种参数数据的传输格式(json、xml等)、是否满足参数字段的格式(长度、字符要求等)
这将非常麻烦,于是就可以使用validate。它支持使用验证标签和自定义验证器来进行跨字段和跨结构体验证。并且内置验证方法,满足基本的验证需求,可以直接调用
用法:
基本的用法:
使用内置的验证函数库+验证tag
一般我们的传输数据格式都是json,eg:
全部代码示例
package mainimport ("github.com/gin-gonic/gin""github.com/go-playground/validator/v10""net/http" )// 参数结构体定义: type User struct {Username string `json:"username" validate:"required,min=4,max=20"` // 必填字段,限制长度Password string `json:"password" validate:"required"` // 必填字段Repassword string `json:"re_password" validate:"required,eqfield=Password"` //必填字段,确认密码 }func main() {router := gin.Default()router.POST("/signup", SignUp)_ = router.Run("127.0.0.1:9000") }//注册函数 func SignUp(ctx *gin.Context) {var user Usererr := ctx.ShouldBindJSON(&user)if err != nil {// 错误:不是json格式的数据ctx.JSON(http.StatusOK, gin.H{"msg": err.Error()})return} // 新建验证器validate := validator.New()// 验证器的方法:可以验证结构体类型,支持嵌套检查err = validate.Struct(user)// 错误:不符合tag定义的规则if err != nil {ctx.JSON(http.StatusOK, gin.H{"msg": err.Error()})return}ctx.JSON(http.StatusOK, gin.H{"msg": "success"}) }
分解理解
安装、导入
可以直接go get,也可以先导入,然后再go mod tidy:这个就是 第三方库的依赖包 "github.com/go-playground/validator/v10"
参数结构体
// 参数结构体定义:用tag进行验证规则的定义,validate是结构体tag type User struct {Username string `json:"username" validate:"required,min=4,max=20"` // 必填字段,限制长度Password string `json:"password" validate:"required"` // 必填字段Repassword string `json:"re_password" validate:"required,eqfield=Password"` //必填字段,确认密码 }
验证
func SignUp(ctx *gin.Context) {var user Usererr := ctx.ShouldBindJSON(&user)if err != nil {ctx.JSON(http.StatusOK, gin.H{"msg": err.Error()})return} // 新建验证器validate := validator.New()// 验证器Struct的方法:可以验证结构体类型,支持嵌套检查err = validate.Struct(user)// 错误:不符合tag定义的规则if err != nil {ctx.JSON(http.StatusOK, gin.H{"msg": err.Error()})return}ctx.JSON(http.StatusOK, gin.H{"msg": "success"}) }
其他
与此类似的还有map验证、slice验证、变量验证、字段验证等
Validator | Golang中文学习文档
进阶的用法:
自定义别名
场景:我们写代码发现:有好多验证字段的验证规则相同,虽然可以直接CV,但是还是不够优雅,这时我们可以利用起别名的方法提高代码复用性
比如:
const LENGTH_RULES = "required,min=4,max=20" // 注册别名 validate.RegisterAlias("length_rules", LENGTH_RULES) type User struct {Username string `json:"username" validate:"required,min=4,max=20"` // 必填字段,限制长度Password string `json:"password" validate:"required"` // 必填字段Repassword string `json:"re_password" validate:"required,eqfield=Password"` //必填字段,确认密码Address string `json:"username" validate:"required,min=4,max=20"` // 必填字段,限制长度 } //直接: type User struct {Username string `json:"username" validate:"length_rules"` // 必填字段,限制长度Password string `json:"password" validate:"required"` // 必填字段Repassword string `json:"re_password" validate:"required,eqfield=Password"` //必填字段,确认密码Address string `json:"username" validate:"length_rules"` // 必填字段,限制长度 }
自定义验证函数
func TestCustomValidate(t *testing.T) {validate = validator.New()//注册验证函数:参数:(名称,方法)validate.RegisterValidation("is666", is666)type Example struct {Name string `validate:"is666"`}fmt.Println(validate.Struct(Example{Name: "777"})) // Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tagfmt.Println(validate.Struct(Example{Name: "666"})) } //自定义验证函数 func is666(fl validator.FieldLevel) bool {return fl.Field().String() == "666" }
自定义类型验证函数
validate = validator.New()// 创建验证器 validate.RegisterCustomTypeFunc(参数1,参数2)//名称,函数 :注册到验证器中 //注意: reflect.Value:这个参数以反射值的形式传递了要验证的对象(在这里就是Address类型的实例对应的反射值) func ValidateAddress(value reflect.Value) interface{} {// 通过类型断言获得结构体实例if address, ok := value.Interface().(Address); ok {//错误处理if address.name == "" {return address.name} return value //返回字段即代表验证正确}return nil }
自定义结构体类型验证函数
RegisterStructValidation(参数1,参数2...)//validate = validator.New() validate.RegisterStructValidation(PeopleValidate, People{})//将自定义的验证函数PeopleValidate注册到验证器中。PeopleValidate就是具体实现验证逻辑的函数,而第二个参数People{}表示这个验证规则是针对People结构体类型的 //注意:validator.StructLevel提供了与当前正在验证的结构体相关的上下文信息 func PeopleValidate(sl validator.StructLevel) {// 反射:获得当前要验证的结构体的信息,通过类型断言获得结构体实例people := sl.Current().Interface().(People) // 验证逻辑:自己根据要求写就好了if people.FirstName == "" || people.LastName == "" {sl.ReportError(people.FirstName, "FirstName", "FirstName", "", "")sl.ReportError(people.FirstName, "LastName", "LastName", "", "")} }
注意:我们会发现其实类型就是结构体,觉得这两个差不多,事实上确实差不多,但是还存在一些细微差异:
一般类型侧重于对单个自定义类型的验证,该类型不一定是结构体,也可能是其他自定义的基础类型或接口类型等。它主要关注该类型自身的特性和约束条件,比如对于一个自定义的整数类型,验证其是否在特定的取值范围内等。
结构体类型:针对某个具体的结构体定义的验证函数,其验证逻辑紧密围绕该结构体的各个字段及其组合关系展开。它主要关注结构体整体的合法性,例如验证结构体中多个字段之间的逻辑关系是否正确,或者某个字段的值是否符合与其他字段相关的特定条件等。
多语言
安装翻译器组件和地区组件并导入
go get github.com/go-playground/universal-translator //翻译器组件 go get github.com/go-playground/locales //地区import ("net/http""github.com/gin-gonic/gin""github.com/go-playground/locales/zh"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zh_trans "github.com/go-playground/validator/v10/translations/zh"// 导入前面代码中的相关包和定义的结构体等lan "gin_demo/validator/language" )导入的相关包: import ( // 验证依赖"github.com/go-playground/validator/v10" // 语言"github.com/go-playground/locales/zh" // 提供了中文语言相关的本地化资源ut "github.com/go-playground/universal-translator" // 创建通用的翻译器实例,以支持多种语言的转换zh_trans "github.com/go-playground/validator/v10/translations/zh" //lan "gin_demo/validator/language" )
验证器默认的语言是英文,而我们在进行项目开发时,可能会用到不止一种语言,这时候我们就需要用到国际化多语言组件,看下面的一个例子:
初始化验证器和翻译器
func InitValidatorAndTranslator() (*validator.Validate, ut.Translator) {zh := zh.New()//汉语言资源:中文本地化相关的实例uni := ut.New(zh, zh)//支持多语言验证错误提示翻译的机制中起着初始化、构建翻译基础框架的重要作用,是实现后续语言转换功能的关键一步。trans, _ := uni.GetTranslator("zh")//获取中文翻译器。validate = validator.New()zh_trans.RegisterDefaultTranslations(validate, trans)return validate, trans }翻译验证错误信息并返回给客户端
c.JSON(http.StatusBadRequest, gin.H{"errors": errs.Translate(trans), })
标签:
字段
Tag | Description |
---|---|
eqcsfield | 在一个单独的结构中,验证当前字段的值是否等于由param的值指定的字段 |
eqfield | 验证当前字段的值是否等于参数值指定的字段 |
fieldcontains | 验证当前字段的值是否包含由参数值指定的字段 |
fieldexcludes | 验证当前字段的值是否不包含由参数值指定的字段 |
gtcsfield | 在一个单独的结构中,验证当前字段的值是否大于由参数的值指定的字段 |
gtecsfield | 在一个单独的结构中,验证当前字段的值是否大于或等于由参数的值指定的字段 |
gtefield | 验证当前字段的值是否大于或等于由参数值指定的字段 |
gtfield | 验证当前字段的值是否大于由参数值指定的字段 |
ltcsfield | 在一个单独的结构中,验证当前字段的值是否小于由参数的值指定的字段 |
ltecsfield | 在一个单独的结构中,验证当前字段的值是否小于等于由参数的值指定的字段 |
ltefield | 验证当前字段的值是否小于或等于由参数值指定的字段 |
ltfield | 验证当前字段的值是否小于由参数值指定的字段 |
necsfield | 验证当前字段的值不等于由参数的值指定的单独结构中的字段 |
nefield | 验证当前字段的值是否不等于参数值指定的字段 |
#网络
Tag | Description |
---|---|
cidr | 无类域间路由CIDR |
cidrv4 | 无类域间路由CIDRv4 |
cidrv6 | 无类域间路由CIDRv6 |
datauri | 数据统一资源定位符 |
fqdn | 完全限定域名(FQDN) |
hostname | 主机名 RFC 952 |
hostname_port | 通常用于套接字地址的字段验证<dns>:<port> 组合 |
hostname_rfc1123 | 主机名 RFC 952 |
ip | 因特网协议地址 IP |
ip4_addr | 因特网协议地址 IPv4 |
ip6_addr | 因特网协议地址 IPv6 |
ip_addr | 因特网协议地址 IP |
ipv4 | 因特网协议地址 IPv4 |
ipv6 | 因特网协议地址 IPv6 |
mac | 媒体存取控制位址,也称局域网地址 |
tcp4_addr | 传输控制协议地址 TCP4 |
tcp6_addr | 传输控制协议地址 TCPv6 |
tcp_addr | 传输控制协议地址 TCP |
udp4_addr | 用户数据报协议地址 UDPv4 |
udp6_addr | 用户数据报协议地址 UDPv6 |
udp_addr | 用户数据报协议地址 UDP |
unix_addr | Unix域套接字端点地址 |
uri | 统一资源标识符 |
url | 统一资源定位符 |
url_encoded | 统一资源标识符编码 |
urn_rfc2141 | RFC 2141 统一资源名 |
#字符串
Tag | Description |
---|---|
alpha | 验证当前字段的值是否是有效的字母 |
alphanum | 验证当前字段的值是否是有效的字母数字 |
alphanumunicode | 验证当前字段的值是否是有效的字母数字unicode值 |
alphaunicode | 验证当前字段的值是否是有效的字母unicode值 |
ascii | 验证字段的值是否为有效的ASCII字符 |
boolean | 验证当前字段的值是否为有效的布尔值或是否可以安全地转换为布尔值 |
contains | 验证字段的值是否包含参数中指定的文本 |
containsany | 验证字段的值是否包含参数中指定的任何字符 |
containsrune | 验证字段的值是否包含参数中指定的符文 |
endsnotwith | 验证字段的值不以参数中指定的文本结束 |
endswith | 验证字段的值以参数中指定的文本结束 |
excludes | 验证字段的值不包含参数中指定的文本 |
excludesall | 验证字段的值不包含参数中指定的任何字符 |
excludesrune | 验证字段的值不包含参数中指定的字符 |
lowercase | 验证当前字段的值是否为小写字符串 |
multibyte | 验证字段的值是否具有多字节字符 |
number | 验证当前字段的值是否为有效数字 |
numeric | 验证当前字段的值是否是有效的数值 |
printascii | 验证字段的值是否是有效的可打印ASCII字符 |
startsnotwith | 验证字段的值不是以参数中指定的文本开始 |
startswith | 验证字段的值是否以参数中指定的文本开始 |
uppercase | 验证当前字段的值是否为大写字符串 |
#格式化
Tag | Description |
---|---|
base64 | Base64 字符串 |
base64url | Base64URL 字符串 |
bic | 验证当前字段的值是否为ISO 9362中定义的有效的BIC码(SWIFT代码) |
bcp47_language_tag | 验证当前字段的值是否为BCP47规范的语言标签 |
btc_addr | 验证字段的值是否为有效的BTC地址 |
btc_addr_bech32 | 验证字段的值是否为有效的bech32 BTC地址 |
credit_card | 验证当前字段的值是否是有效的信用卡号 |
datetime | 验证当前字段的值是否是有效的时间日期字符串 |
e164 | 验证当前字段的值是否为有效的e.164格式的电话号码 |
email | 验证当前字段的值是否是有效的电子邮件地址 |
eth_addr | 验证字段的值是否为有效的以太坊地址 |
hexadecimal | 验证当前字段的值是否为有效的十六进制 |
hexcolor | 验证当前字段的值是否是有效的十六进制颜色 |
hsl | 验证当前字段的值是否是有效的HSL颜色 |
hsla | 验证当前字段的值是否是有效的HSLA颜色 |
html | 验证当前字段的值是否是有效的HTML |
html_encoded | 验证当前字段的值是否是有效的HTML编码 |
isbn | 验证字段的值是否为有效的v10或v13 ISBN(国际标准书号) |
isbn10 | 验证字段的值是否为有效的v10 ISBN(国际标准书号) |
isbn13 | 验证字段的值是否为有效的v13 ISBN(国际标准书号) |
iso3166_1_alpha2 | 验证当前字段的值是否为有效的iso3166-1 alpha-2国家代码 |
iso3166_1_alpha3 | 验证当前字段的值是否为有效的iso3166-1 alpha-3国家代码 |
iso3166_1_alpha_numeric | 验证当前字段的值是否为有效的iso3166-1字母数字国家代码 |
iso3166_2 | 验证当前字段的值是否为有效的国家地区代码 (ISO 3166-2) |
iso4217 | 验证当前字段的值是否为有效的货币代码 (ISO 4217) |
json | 验证当前字段的值是否为有效的json字符串 |
jwt | 验证当前字段的值是否是有效的JWT字符串 |
latitude | 验证字段的值是否是有效的纬度坐标 |
longitude | 验证字段的值是否是有效的纬度坐标 |
postcode_iso3166_alpha2 | 根据iso 3166 alpha 2中国家代码的值进行验证 |
postcode_iso3166_alpha2_field | 通过字段验证,该字段表示iso 3166 alpha 2中的国家代码值 |
rgb | 验证当前字段的值是否是有效的RGB颜色 |
rgba | 验证当前字段的值是否是有效的RGBA颜色 |
ssn | 验证字段的值是否是有效的SSN |
timezone | 验证当前字段的值是否是有效的时区字符串 |
uuid | 验证字段的值是否是任何版本的有效UUID |
uuid3 | 验证字段的值是否是任的有效UUID v3 |
uuid3_rfc4122 | 验证字段的值是否为有效的RFC4122 v3 UUID |
uuid4 | 验证字段的值是否为有效的v4 UUID |
uuid4_rfc4122 | 验证字段的值是否为有效的RFC4122 v4 UUID |
uuid5 | 验证字段的值是否是有效的v5 UUID |
uuid5_rfc4122 | 验证字段的值是否是有效的RFC4122 v5 UUID |
uuid_rfc4122 | 验证字段的值是否为任何版本的有效RFC4122 UUID |
md4 | 验证字段的值是否为有效的MD4 |
md5 | 验证字段的值是否为有效的MD5 |
sha256 | 验证该字段的值是否是有效的SHA256 |
sha384 | 验证字段的值是否是有效的SHA384 |
sha512 | 验证字段的值是否为有效的SHA512 |
ripemd128 | 验证字段的值是否是有效的PIPEMD128 |
ripemd128 | 验证字段的值是否是有效的PIPEMD160 |
tiger128 | 验证字段的值是否是有效的TIGER128 |
tiger160 | 验证字段的值是否是有效的TIGER160 |
tiger192 | 验证字段的值是否是有效的TIGER192 |
semver | 验证当前字段的值是否为语义版本2.0.0中定义的有效semver版本 |
ulid | 验证字段的值是否为有效的ULID |
#比较
Tag | Description |
---|---|
eq | 等于 |
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
ne | 不等于 |
#其他
Tag | Description |
---|---|
dir | 文件目录 |
file | 文件路径 |
isdefault | 验证当前字段的值是否是默认静态值 |
len | 字段长度 |
max | 最大值 |
min | 最小值 |
oneof | 是否是列举的值的其中的一个 |
oimtempty | 如果字段未设置,则忽略它 |
required | 必须值 |
required_if | 只有当所有其他指定字段与指定字段后面的值相等时,验证的字段必须存在且不为空 |
required_unless | 除非所有其他指定字段与指定字段后面的值相等,验证的字段必须存在且不为空 |
required_with | 当指定的字段有一个存在时,验证的字段必须存在且不为空 |
required_with_all | 当指定的所有字段存在时,验证的字段必须存在且不为空 |
required_without | 当指定的字段有一个不存在时,验证的字段必须存在且不为空 |
required_without_all | 当指定的字段全部不存在时,验证的字段必须存在且不为空 |
excluded_if | 只有当所有其他指定字段与指定字段后面的值相等时,验证的字段可以不存在或者为空 |
excluded_unless | 除非所有其他指定字段与指定字段后面的值相等,验证的字段可以不存在或者为空 |
excluded_with | 当指定的字段有一个存在时,验证的字段可以不存在或者为空 |
excluded_with_all | 当指定的所有字段存在时,验证的字段可以不存在或者为空 |
excluded_without | 当指定的字段有一个不存在时,验证的字段可以不存在或者为空 |
excluded_without_all | 当指定的字段全部不存在时,验证的字段可以不存在或者为空 |
unique | 验证每个`arr |
#别名
Tag | Description |
---|---|
iscolor | hexcolor|rgb|rgba|hsl|hsla |
country_code | iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric |
#操作符
Tag | Description | Hex |
---|---|---|
, | 与操作,使用多个验证标记,必须所有条件都满足,隔开逗号之间不能有空格 | 0x2c |
| | 或操作,使用多个验证标记,但是只需满足其中一个即可 | |
- | 该字段跳过验证 | 0x2d |
= | 参数匹配符号 | 0x3d |