结构字段验证--validator.v9

官网:https://godoc.org/gopkg.in/go-playground/validator.v9#hdr-Baked_In_Validators_and_Tags

package validator

validator.v9: gopkg.in/go-playground/validator.v9

使用:import "gopkg.in/go-playground/validator.v9"

validator包基于标签实现结构和单个字段的值验证。 它还可以处理嵌套结构的跨场验证和跨结构验证,并且能够深入了解任何类型的数组和映射。

更多例子:https://github.com/go-playground/validator/tree/v9/_examples

验证函数返回类型错误 这样做的方式实际上就是标准库的工作方式,请参见file.Open方法:

https://golang.org/pkg/os/#Open.

 
作者返回类型“错误”以避免以下讨论的问题,其中 error 总是 != nil:

http://stackoverflow.com/a/29138676/3158232
https://github.com/go-playground/validator/issues/134

 验证器仅用于错误验证输入的InvalidValidationError,nil或ValidationErrors作为类型错误;因此,在您的代码中,您需要做的就是检查返回的错误是否为nil,如果不检查错误是否为InvalidValidationError(如果需要,大部分时间不是),请键入类型为ValidationErrors,如此ERR。(validator.ValidationErrors)。

自定义验证功能

可以添加自定义验证功能。例:

// 结构体
func customFunc(fl validator.FieldLevel) bool {

    if fl.Field().String() == "invalid" {
        return false
    }

    return true
}

validate.RegisterValidation("custom tag name", customFunc)
// 注意:使用与现有函数相同的标记名称将覆盖现有函数

完整示例:

package main

import (
    "fmt"
    "gopkg.in/go-playground/validator.v9"
)

type User struct {
    Name string `validate:"is-zhou"`
}

func (u *User) userValidator() error {
    validate := validator.New()
    validate.RegisterValidation("is-zhou", ValidateMyVal)
    err := validate.Struct(u)
    return err
}
// ValidateMyVal implements validator.Func
func ValidateMyVal(fl validator.FieldLevel) bool {
    return fl.Field().String() == "zhou"
}

func main() {
    user1 := User{
        Name:"zhou",
    }
    if err := user1.userValidator(); err != nil {
        fmt.Println("user1", err)
    }

    user2 := User{
        Name:"zhou1",
    }
    if err := user2.userValidator(); err != nil {
        fmt.Println("user2", err)
    }
}

打印结果:

user2 Key: 'User.Name' Error:Field validation for 'Name' failed on the 'is-zhou' tag

跨字段验证

跨字段验证可以通过以下标签完成: 

- eqfield
- nefield
- gtfield
- gtefield
- ltfield
- ltefield
- eqcsfield
- necsfield
- gtcsfield
- gtecsfield
- ltcsfield
- ltecsfield

但是,如果需要某些自定义跨字段验证,则可以使用自定义验证来完成。
为什么不只是有跨字段验证标签(即只有eqcsfield而不是eqfield)?
原因是效率。如果要检查同一结构中的字段“eqfield”,只需在同一结构(1级)上找到该字段。但是,如果我们使用“eqcsfield”,它可能会降低多个级别。例:

type Inner struct {
    StartDate time.Time
}

type Outer struct {
    InnerStructField *Inner
    CreatedAt time.Time      `validate:"ltecsfield=InnerStructField.StartDate"`
}

now := time.Now()

inner := &Inner{
    StartDate: now,
}

outer := &Outer{
    InnerStructField: inner,
    CreatedAt: now,
}

errs := validate.Struct(outer)
// 注意:调用validate.Struct(val)时,topStruct将是传递给函数的顶级结构,
// 当调用validate.VarWithValue(val,field,tag)时,val将是你传递的任何东西,比如: struct,field ......
// 当调用validate.Field(field,tag)时,val将为nil

多个验证器

字段上的多个验证器将按定义的顺序处理。例:

ype Test struct {
    Field `validate:"max=10,min=1"`
}

// 先验证max,然后才验证min

错误的验证不会处理,例如:

type Test struct {
    Field `validate:"min=10,max=0"`
}

// this definition of min max will never succeed

使用验证器标签:

跨字段验证仅比较同一结构上的字段。如果需要不同结构不同字段比较验证,您应该实现自己的自定义验证器。
逗号(“,”)是验证标记的默认分隔符。如果你希望在参数中包含一个逗号(即excludesall =,),你将需要使用UTF-8十六进制表示0x2C,它在代码中被替换为逗号,因此上面将变为excludesall = 0x2C。

type Test struct {
    Field `validate:"excludesall=,"`    // 错误用法,不能包含逗号
    Field `validate:"excludesall=0x2C"` // 正确用法,使用UTF-8十六进制表示
}

管道(“|”)是'或'验证标签的分隔符。如果您希望在参数中包含管道,即excludesall = |你将需要使用UTF-8十六进制表示0x7C,它在代码中被替换为管道,所以上面将成为excludesall = 0x7C

type Test struct {
    Field `validate:"excludesall=|"`    // BAD! Do not include a a pipe!
    Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation.
}

Baked In Validators and Tags

以下是当前内置验证器的列表:

-        //忽略字段,告诉验证跳过这个struct字段;这对于忽略嵌入式结构的验证尤其方便。 (用法: - )
|        //这是'or'运算符,允许使用和接受多个验证器。 (用法:rbg | rgba)< - 这将允许接受rgb或rgba颜色。这也可以与'and'结合使用(例如:用法:omitempty,rgb | rgba)
structonly    //当遇到嵌套结构的字段并包含此标志时,将运行嵌套结构上的任何验证,但不会验证任何嵌套结构字段。如果您在程序内部知道结构有效,但需要验证它是否已分配,这非常有用。注意:结构本身只能使用“required”和“omitempty”。
nostructlevel    //与structonly标记相同,但不会运行任何结构级别验证。
omitempty    //允许条件验证,例如,如果字段未设置值(由“required”验证器确定),则其他验证(如min或max)将不会运行,但如果设置了值,则验证将运行。
dive    //这告诉验证者潜入切片,数组或映射,并使用后面的验证标记验证切片,数组或映射的该级别。还支持多维嵌套,您希望dive的每个级别都需要另一个dive标签。dive有一些子标签,'keys'和'endkeys',请参阅下面的keys和endkeys部分

----

required    //这将验证该值不是数据类型的默认零值。数字不为0,字符串不为 " ", slices, maps, pointers, interfaces, channels and functions 不为 nil
isdefault    //这验证了该值是默认值,几乎与所需值相反。
len=10    //对于数字,长度将确保该值等于给定的参数。对于字符串,它会检查字符串长度是否与字符数完全相同。对于切片,数组和map,验证元素个数。
max=10    //对于数字,max将确保该值小于或等于给定的参数。对于字符串,它会检查字符串长度是否最多为该字符数。对于切片,数组和map,验证元素个数。
min=10
eq=10    //对于字符串和数字,eq将确保该值等于给定的参数。对于切片,数组和map,验证元素个数。
ne=10    //和eq相反
oneof=red green (oneof=5 7 9)    //对于字符串,整数和uint,oneof将确保该值是参数中的值之一。参数应该是由空格分隔的值列表。值可以是字符串或数字。
gt=10    //对于数字,这将确保该值大于给定的参数。对于字符串,它会检查字符串长度是否大于该字符数。对于切片,数组和map,它会验证元素个数。
gt    //对于time.Time确保时间值大于time.Now.UTC()
gte=10    //大于等于
gte    //对于time.Time确保时间值大于或等于time.Now.UTC()
lt=10    //小于
lt    //对于time.Time确保时间值小于time.Now.UTC()
lte=10    //小于等于
lte    //对于time.Time确保时间值小于等于time.Now.UTC()

----

unique    //对于数组和切片,unique将确保没有重复项。对于map,unique将确保没有重复值。
alpha    //这将验证字符串值是否仅包含ASCII字母字符
alphanum    //这将验证字符串值是否仅包含ASCII字母数字字符
alphaunicode    //这将验证字符串值是否仅包含unicode字符
alphanumunicode    //这将验证字符串值是否仅包含unicode字母数字字符
numeric    //这将验证字符串值是否包含基本数值。基本排除指数等...对于整数或浮点数,它返回true。
hexadecimal    //这将验证字符串值是否包含有效的十六进制
hexcolor    //这验证字符串值包含有效的十六进制颜色,包括#标签(#)
rgb    //这将验证字符串值是否包含有效的rgb颜色
rgba    //这将验证字符串值是否包含有效的rgba颜色
hsl    //这将验证字符串值是否包含有效的hsl颜色
hsla    //这将验证字符串值是否包含有效的hsla颜色
email    //这验证字符串值包含有效的电子邮件这可能不符合任何rfc标准的所有可能性,但任何电子邮件提供商都不接受所有可能性
file    //这将验证字符串值是否包含有效的文件路径,并且该文件存在于计算机上。这是使用os.Stat完成的,它是一个独立于平台的函数。
url    //这会验证字符串值是否包含有效的url这将接受golang请求uri接受的任何url,但必须包含一个模式,例如http://或rtmp://
uri    //这验证了字符串值包含有效的uri。这将接受uri接受的golang请求的任何uri
base64    //这将验证字符串值是否包含有效的base64值。虽然空字符串是有效的base64,但这会将空字符串报告为错误,如果您希望接受空字符串作为有效字符,则可以将此字符串与omitempty标记一起使用。
base64url    //这会根据RFC4648规范验证字符串值是否包含有效的base64 URL安全值。尽管空字符串是有效的base64 URL安全值,但这会将空字符串报告为错误,如果您希望接受空字符串作为有效字符,则可以将此字符串与omitempty标记一起使用。
btc_addr    //这将验证字符串值是否包含有效的比特币地址。检查字符串的格式以确保它匹配P2PKH,P2SH三种格式之一并执行校验和验证
btc_addr_bech32    //这验证了字符串值包含bip-0173定义的有效比特币Bech32地址(https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)特别感谢Pieter Wuille提供的参考实现。
eth_addr    //这将验证字符串值是否包含有效的以太坊地址。检查字符串的格式以确保它符合标准的以太坊地址格式完全验证被https://github.com/golang/crypto/pull/28阻止
contains=@    //这将验证字符串值是否包含子字符串值
containsany=!@#?    //这将验证字符串值是否包含子字符串值中的任何Unicode code points。
containsrune=@    //这将验证字符串值是否包含提供的符文值。
excludes=@    //这验证字符串值不包含子字符串值。
excludesall=!@#?    //这将验证字符串值在子字符串值中是否包含任何Unicode code points。
excludesrune=@    //这将验证字符串值是否包含提供的符文值。

-----

isbn    //这将验证字符串值是否包含有效的isbn10或isbn13值。
isbn10    //这将验证字符串值是否包含有效的isbn10值。
isbn13    //这将验证字符串值是否包含有效的isbn13值。
uuid    //这将验证字符串值是否包含有效的UUID。
uuid3    //这将验证字符串值是否包含有效的版本3 UUID。
uuid4    //这将验证字符串值是否包含有效的版本4 UUID。
uuid5    //这将验证字符串值是否包含有效的版本5 UUID。

-----

ascii    //这将验证字符串值是否仅包含ASCII字符。注意:如果字符串为空,则验证为true
printascii    //这将验证字符串值是否仅包含可打印的ASCII字符。注意:如果字符串为空,则验证为true。
multibyte    //这将验证字符串值是否包含一个或多个多字节字符。注意:如果字符串为空,则验证为true
datauri    //这将验证字符串值是否包含有效的DataURI。注意:这也将验证数据部分是否有效base64
latitude    //这将验证字符串值是否包含有效的纬度。
longitude    //这将验证字符串值是否包含有效经度。
ssn    //这将验证字符串值是否包含有效的美国社会安全号码。
ip    //这将验证字符串值是否包含有效的IP地址
ipv4    //这将验证字符串值是否包含有效的v4 IP地址
ipv6    //这将验证字符串值是否包含有效的v6 IP地址
cidr    //这将验证字符串值是否包含有效的CIDR地址
cidrv4    //这将验证字符串值是否包含有效的v4 CIDR地址
cidrv5    //这将验证字符串值是否包含有效的v5 CIDR地址
tcp_addr    //这将验证字符串值是否包含有效的可解析TCP地址
tcp4_addr    //这将验证字符串值是否包含有效的可解析v4 TCP地址
tcp6_addr    //这将验证字符串值是否包含有效的可解析v6 TCP地址
udp_addr    //这将验证字符串值是否包含有效的可解析UDP地址
udp4_addr    //这将验证字符串值是否包含有效的可解析v4 UDP地址
udp6_addr    //这将验证字符串值是否包含有效的可解析v6 UDP地址
ip_addr    //这将验证字符串值是否包含有效的可解析IP地址
ip4_addr    //这将验证字符串值是否包含有效的可解析v4 IP地址
ip6_addr    //这将验证字符串值是否包含有效的可解析v6 IP地址
unix_addr    //这将验证字符串值是否包含有效的Unix地址

------

mac    //这将验证字符串值是否包含有效的MAC地址
//注意:有关可接受的格式和类型,请参阅Go的ParseMAC: http://golang.org/src/net/mac.go?s=866:918#L29
hostname    //根据RFC 952 https://tools.ietf.org/html/rfc952验证字符串值是否为有效主机名
hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias    //根据RFC 1123 https://tools.ietf.org/html/rfc1123验证字符串值是否为有效主机名
fqdn    //这将验证字符串值是否包含有效的FQDN (完全合格的有效域名),Full Qualified Domain Name (FQDN)
html    //这将验证字符串值是否为HTML元素标记,包括https://developer.mozilla.org/en-US/docs/Web/HTML/Element中描述的标记。
html_encoded    //这将验证字符串值是十进制或十六进制格式的正确字符引用
url_encoded    //这验证了根据https://tools.ietf.org/html/rfc3986#section-2.1对字符串值进行了百分比编码(URL编码)

dive

例1:

[][]string with validation tag "gt=0,dive,len=1,dive,required"
// gt=0 被用于验证 []
// len=1 被用于验证 []string
// required 被用于验证 string

例2:

[][]string with validation tag "gt=0,dive,dive,required"
// gt=0 被用于验证 []
// []string 将被保留验证
// required  被用于验证 string

Keys & EndKeys
这些将在dive标签之后直接使用,并告诉验证者“keys”和“endkeys”之间的任何内容都适用于map的key而不是value;把它想象成'dive'标签,但是对于map的keys而不是values。
还支持多维嵌套,您希望验证的每个级别都需要另一个“keys”和“endkeys”标记。这些标签仅对map有效。

用法:dive,keys,othertagvalidation(s),endkeys,valuevalidationtags

例1:

map[string]string with validation tag "gt=0,dive,keys,eg=1|eq=2,endkeys,required"
// gt=0 will be applied to the map itself
// eg=1|eq=2 will be applied to the map keys
// required will be applied to map values

例2:

map[[2]string]string with validation tag "gt=0,dive,keys,dive,eq=1|eq=2,endkeys,required"
// gt=0 will be applied to the map itself
// eg=1|eq=2 will be applied to each array element in the the map keys
// required will be applied to map values

Field Equals Another Field

这将在结构内或传入字段中针对另一个字段值验证字段值。

例1:

// 使用以下方法验证密码字段:
Usage: eqfield=ConfirmPassword
// 按字段验证:
validate.VarWithValue(password, confirmpassword, "eqfield")

字段等于另一字段(相对)

这与eqfield相同,只是它验证了相对于顶级结构提供的字段。

Usage: eqcsfield=InnerStructField.Field)

字段不等于另一个字段

这将在结构内或传入字段中针对另一个字段值验证字段值。

// 确认两种颜色不一样:
// 对色域进行验证:
Usage: nefield=Color2

// 按字段验证:
validate.VarWithValue(color1, color2, "nefield")

字段不等于另一个字段(相对)

这与nefield相同,只是它验证了相对于顶级结构提供的字段。

Usage: necsfield=InnerStructField.Field

字段大于另一字段

仅对Numbers和time.Time类型有效,这将在结构内或传入字段中针对另一个字段值验证字段值。用法示例用于验证开始日期和结束日期:

例1:

// 用于验证结束字段(对比开始字段):
validate.Struct Usage(gtfield=Start)

例2:

// 按字段验证:
validate.VarWithValue(start, end, "gtfield")

字段大于另一个相对字段

这与gtfield相同,不同之处在于它验证了相对于顶级结构提供的字段。

Usage: gtcsfield=InnerStructField.Field

字段大于或等于另一字段

仅对Numbers和time.Time类型有效,这将在结构内或传入字段中针对另一个字段值验证字段值。用法示例用于验证开始日期和结束日期: 

 例1:

// 用于验证结束字段(相对开始字段):
validate.Struct Usage(gtefield=Start)

例2:

// 通过字段验证:
validate.VarWithValue(start, end, "gtefield")

字段大于或等于另一个相对字段

这与gtefield相同,只是它验证了相对于顶级结构提供的字段。

Usage: gtecsfield=InnerStructField.Field

小于另一字段

仅对Numbers和time.Time类型有效,这将在结构内或传入字段中针对另一个字段值验证字段值。用法示例用于验证开始日期和结束日期:

例1:

// Validation on End field using:
validate.Struct Usage(ltfield=Start)

例2:

// Validating by field:
validate.VarWithValue(start, end, "ltfield")

小于另一相对字段

这与ltfield相同,只是它验证了相对于顶级结构提供的字段。

Usage: ltcsfield=InnerStructField.Field

小于等于另一字段

仅对Numbers和time.Time类型有效,这将在结构内或传入字段中针对另一个字段值验证字段值。用法示例用于验证开始日期和结束日期:

例1:

// Validation on End field using:
validate.Struct Usage(ltefield=Start)

例2:

// Validating by field:
validate.VarWithValue(start, end, "ltefield")

小于等于另一相对字段

这与ltefield相同,只是它验证了相对于顶级结构提供的字段。

Usage: ltecsfield=InnerStructField.Field

======

Alias Validators and Tags(别名验证器和标签) 

注意:返回error时,“FieldError”中返回的tag将是alias tag,除非dive tag是alias的一部分。dive tag之后的所有内容都不会报告为alias tag。此外,前一种情况中的“ActualTag”将是失败的alias中的实际tag。 以下是当前内置 alias tag 的列表:

"iscolor"
    alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor)

Validator notes:

regex
    a regex validator won't be added because commas and = signs can be part
    of a regex which conflict with the validation definitions. Although
    workarounds can be made, they take away from using pure regex's.
    Furthermore it's quick and dirty but the regex's become harder to
    maintain and are not reusable, so it's as much a programming philosophy
    as anything.

    In place of this new validator functions should be created; a regex can
    be used within the validator function and even be precompiled for better
    efficiency within regexes.go.

    And the best reason, you can submit a pull request and we can keep on
    adding to the validation library of this package!

Panics

This package panics when bad input is provided, this is by design, bad code like that should not make it to production.

type Test struct {
    TestField string `validate:"nonexistantfunction=1"`
}

t := &Test{
    TestField: "Test"
}

validate.Struct(t) // this will panic

 

posted @ 2018-11-28 17:03  静静别跑  阅读(11755)  评论(2编辑  收藏  举报