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
email 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
此页面最后编辑于2022年10月26日 (星期三) 11:02。