Golang go-playground validator 包中文教程
本页内容
在Web开发中经常会对用户输入数据进行校验,或者在提供API给第三方使用时候需要对字段进行校验,防止脏数据和恶意请求。 Golang官方维护了 go-playground/validator[1] 是使用最广泛数据校验的包,自带了非常多常用的校验规则。其基于标签实现结构和单个字段的值验证。
特性
- 使用验证标签或自定义验证器进行跨字段和跨结构验证。
- slice、array 和 map 层级校验支持,允许验证多维字段的任何或所有级别。
- 支持对 map key 和 value 以进行验证
- 支持处理自定义字段类型,例如 sql driver
- 重命名验证标签,允许将多个验证映射到单个标签,以便更轻松地定义struct上的验证规则
- 支持提取验证数据字段的名称,例如可以指定在验证时提取 JSON 名称,并使其在生成的 FieldError 中可用
- 可设置的 i18n 错误消息。
- gin web 框架的默认验证器
安装
示例
go get github.com/go-playground/validator/v10
代码中引入包
import "github.com/go-playground/validator/v10"
简单的用法示例[2]
可以看出我们只用在结构体的字段上加上对应的`validate:"xx"`即可添加对应验证规则。
示例
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
validateStruct()
validateVariable()
}
func validateStruct() {
address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
}
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
Email: "[email protected]",
FavouriteColor: "#000-",
Addresses: []*Address{address},
}
// returns nil or ValidationErrors ( []FieldError )
err := validate.Struct(user)
if err != nil {
// this check is only needed when your code could produce
// an invalid value for validation such as interface with nil
// value most including myself do not usually have code like this.
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Type())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
}
// from here you can create your own error messages in whatever language you wish
return
}
// save user to database
}
func validateVariable() {
myEmail := "joeybloggs.gmail.com"
errs := validate.Var(myEmail, "required,email")
if errs != nil {
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
return
}
// email ok, move on
}
验证规则
字段验证规则
提示: cs为cross-struct, 跨字段验证。
eqfield=ConfirmPassword
eqcsfield=InnerStructField.Field
标签 | 说明 |
---|---|
eqcsfield | 字段等于另一个字段(相对) |
eqfield | 字段等于另一个字段 |
fieldcontains | 字段包含 |
fieldexcludes | 字段不包含 |
gtcsfield | 字段大于另一个相对字段 |
gtecsfield | 大于或等于另一个相对字段的字段 |
gtefield | 大于或等于另一个字段的字段 |
gtfield | 字段大于其他字段 |
ltcsfield | 小于另一个相对字段 |
ltecsfield | 小于或等于另一个相对字段 |
ltefield | 小于或等于另一个字段 |
ltfield | 小于另一个字段 |
necsfield | 字段不等于另一个字段(相对) |
nefield | 字段不等于其他字段 |
网络相关验证规则
Tag | Description |
---|---|
cidr | Classless Inter-Domain Routing CIDR |
cidrv4 | Classless Inter-Domain Routing CIDRv4 |
cidrv6 | Classless Inter-Domain Routing CIDRv6 |
datauri | Data URL |
fqdn | Full Qualified Domain Name (FQDN) |
hostname | Hostname RFC 952 |
hostname_port | HostPort |
hostname_rfc1123 | Hostname RFC 1123 |
ip | Internet Protocol Address IP |
ip4_addr | Internet Protocol Address IPv4 |
ip6_addr | Internet Protocol Address IPv6 |
ip_addr | Internet Protocol Address IP |
ipv4 | Internet Protocol Address IPv4 |
ipv6 | Internet Protocol Address IPv6 |
mac | Media Access Control Address MAC |
tcp4_addr | Transmission Control Protocol Address TCPv4 |
tcp6_addr | Transmission Control Protocol Address TCPv6 |
tcp_addr | Transmission Control Protocol Address TCP |
udp4_addr | User Datagram Protocol Address UDPv4 |
udp6_addr | User Datagram Protocol Address UDPv6 |
udp_addr | User Datagram Protocol Address UDP |
unix_addr | Unix domain socket end point Address |
uri | URI String |
url | URL String |
url_encoded | URL Encoded |
urn_rfc2141 | Urn RFC 2141 String |
Strings
标签 | 描述 |
---|---|
alpha | 仅字幕 |
alphanum | 字母数字 |
alphanumunicode | Unicode字母数字 |
alphaunicode | Unicode字母 |
ascii | ASCII |
boolean | Boolean |
contains | 包含 |
containsany | 包含任何数据 |
containsrune | 包含rune字符 |
endsnotwith | 结尾不为 |
endswith | 结尾为 |
excludes | 排除 |
excludesall | 全部排除 |
excludesrune | 排除rune字符 |
lowercase | 小写字母 |
multibyte | 多字节字符 |
number | 数字 |
numeric | 数字 |
printascii | 可打印ASCII |
startsnotwith | 开头不是 |
startswith | 开头为 |
uppercase | 大写 |
格式
标签 | 描述 |
---|---|
base64 | Base64 String |
base64url | Base64URL String |
bic | Business Identifier Code (ISO 9362) |
bcp47_language_tag | Language tag (BCP 47) |
btc_addr | Bitcoin Address |
btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
credit_card | Credit Card Number |
datetime | Datetime |
e164 | e164 formatted phone number |
E-mail String | |
eth_addr | Ethereum Address |
hexadecimal | Hexadecimal String |
hexcolor | Hexcolor String |
hsl | HSL String |
hsla | HSLA String |
html | HTML Tags |
html_encoded | HTML Encoded |
isbn | International Standard Book Number |
isbn10 | International Standard Book Number 10 |
isbn13 | International Standard Book Number 13 |
iso3166_1_alpha2 | Two-letter country code (ISO 3166-1 alpha-2) |
iso3166_1_alpha3 | Three-letter country code (ISO 3166-1 alpha-3) |
iso3166_1_alpha_numeric | Numeric country code (ISO 3166-1 numeric) |
iso3166_2 | Country subdivision code (ISO 3166-2) |
iso4217 | Currency code (ISO 4217) |
json | JSON |
jwt | JSON Web Token (JWT) |
latitude | Latitude |
longitude | Longitude |
postcode_iso3166_alpha2 | Postcode |
postcode_iso3166_alpha2_field | Postcode |
rgb | RGB String |
rgba | RGBA String |
ssn | Social Security Number SSN |
timezone | Timezone |
uuid | Universally Unique Identifier UUID |
uuid3 | Universally Unique Identifier UUID v3 |
uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 |
uuid4 | Universally Unique Identifier UUID v4 |
uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 |
uuid5 | Universally Unique Identifier UUID v5 |
uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
md4 | MD4 hash |
md5 | MD5 hash |
sha256 | SHA256 hash |
sha384 | SHA384 hash |
sha512 | SHA512 hash |
ripemd128 | RIPEMD-128 hash |
ripemd128 | RIPEMD-160 hash |
tiger128 | TIGER128 hash |
tiger160 | TIGER160 hash |
tiger192 | TIGER192 hash |
semver | Semantic Versioning 2.0.0 |
ulid | Universally Unique Lexicographically Sortable Identifier ULID |
比较
标签 | 描述 |
---|---|
eq | 等于 |
gt | 大于 |
gte | 大于或等于 |
lt | 少于 |
lte | 小于或等于 |
ne | 不等于 |
其他
标签 | 描述 |
---|---|
dir | 目录 |
file | 文件路径 |
isdefault | 是默认值 |
len | 长度 |
max | 最大值 |
min | 最小值 |
oneof | 其中的一个 |
required | 要求的 |
required_if | 如果有,则需要 |
required_unless | required_unless |
required_with | Required With |
required_with_all | Required With All |
required_without | Required Without |
required_without_all | Required Without All |
excluded_if | Excluded If |
excluded_unless | Excluded Unless |
excluded_with | Excluded With |
excluded_with_all | Excluded With All |
excluded_without | Excluded Without |
excluded_without_all | Excluded Without All |
unique | 唯一的 |
别名
别名 | 标签集合 |
---|---|
iscolor | rgb|rgba|hsl|hsla |
country_code | iso3166_1_alpha3|iso3166_1_alpha_numeric |
自定义验证规则
对于没有定义的验证规则标签,可以自己去定义验证规则和标签名
示例
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
// 设置验证的tag为is-awesome
type MyStruct struct {
String string `validate:"is-awesome"`
}
// 使用单例,这样的好处是会缓存 struct 的数据。
var validate *validator.Validate
func main() {
validate = validator.New()
// 注册验证tag
validate.RegisterValidation("is-awesome", ValidateMyVal)
s := MyStruct{String: "awesome"}
err := validate.Struct(s)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
s.String = "not awesome"
err = validate.Struct(s)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}
}
// ValidateMyVal 实现 validator.Func
func ValidateMyVal(fl validator.FieldLevel) bool {
return fl.Field().String() == "awesome"
}
国际化中文提示信息支持
示例
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
// use a single instance , it caches struct info
var (
uni *ut.UniversalTranslator
validate *validator.Validate
)
func main() {
// NOTE: ommitting allot of error checking for brevity
zh := zh.New()
uni = ut.New(zh, zh)
// this is usually know or extracted from http 'Accept-Language' header
// also see uni.FindTranslator(...)
trans, _ := uni.GetTranslator("zh")
validate = validator.New()
zh_translations.RegisterDefaultTranslations(validate, trans)
translateAll(trans)
translateIndividual(trans)
translateOverride(trans) // yep you can specify your own in whatever locale you want!
}
func translateAll(trans ut.Translator) {
type User struct {
Username string `validate:"required"`
Tagline string `validate:"required,lt=10"`
Tagline2 string `validate:"required,gt=1"`
}
user := User{
Username: "Joeybloggs",
Tagline: "This tagline is way too long.",
Tagline2: "1",
}
err := validate.Struct(user)
if err != nil {
// translate all error at once
errs := err.(validator.ValidationErrors)
// returns a map with key = namespace & value = translated error
// NOTICE: 2 errors are returned and you'll see something surprising
// translations are i18n aware!!!!
// eg. '10 characters' vs '1 character'
fmt.Println(errs.Translate(trans))
}
}
func translateIndividual(trans ut.Translator) {
type User struct {
Username string `validate:"required"`
}
var user User
err := validate.Struct(user)
if err != nil {
errs := err.(validator.ValidationErrors)
for _, e := range errs {
// can translate each error one at a time.
fmt.Println(e.Translate(trans))
}
}
}
func translateOverride(trans ut.Translator) {
validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("required", fe.Field())
return t
})
type User struct {
Username string `validate:"required"`
}
var user User
err := validate.Struct(user)
if err != nil {
errs := err.(validator.ValidationErrors)
for _, e := range errs {
// can translate each error one at a time.
fmt.Println(e.Translate(trans))
}
}
}
字段校验提示会变为中文
map[User.Tagline:Tagline长度必须小于10个字符 User.Tagline2:Tagline2长度必须大于1个字符]
Username为必填字段
Username must have a value!
支持的语言
- ar
- en
- es
- fa
- fr
- id
- it
- ja
- nl
- pt
- pt_BR
- ru
- tr
- vi
- zh
- zh_tw