godoc里可以搜到若干相似的第三方数据校验模块,但笔者推荐使用asaskevich/govalidator,原因:
// 下载
go get github.com/asaskevich/govalidator
注意:查看使用方法到github,查看支持的函数列表到godoc
https://github.com/asaskevich/govalidator
https://godoc.org/github.com/asaskevich/govalidator
govalidator支持非常多种字符串匹配,先贴上一个简单例子
package main
import (
"fmt"
"github.com/asaskevich/govalidator"
)
func main() {
// 判断字符串值是否为合法的IPv4地址
ip4 := "192.168.1.1"
fmt.Println(govalidator.IsIPv4(ip4)) // true
// 判断字符串值是否为合法的MAC
mac := "aa:bb:cc:dd:ee:ffffff"
fmt.Println(govalidator.IsMAC(mac)) // false
// 判断数字是否在指定范围内
dig := 101 // string类型也可以用
fmt.Println(govalidator.InRange(dig, 0, 100)) // false
}
输出
true
false
false
govalidator专门提供了一个函数,用于校验struct的元素
govalidator.ValidateStruct()
简单例子
package main
import (
"fmt"
"github.com/asaskevich/govalidator"
)
type foo struct {
A string `valid:"ipv4"`
B string `valid:"mac"`
C string `valid:"range(0|100)"` // 也可以使用int类型
}
func main() {
f := foo{
A: "192.168.1.1",
B: "aa:bb:cc:dd:ee:ffffff",
C: "101",
}
result, err := govalidator.ValidateStruct(f)
if err != nil {
fmt.Println("error: " + err.Error())
}
fmt.Println(result)
}
输出
error: B: aa:bb:cc:dd:ee:ffffff does not validate as mac;C: 101 does not validate as range(0|100)
false
注意:
▪ struct元素只支持部分常用的校验,详见本文附录2
▪ struct元素必须是导出型,也就是必须大写字母开头,govalidator才会去理会
▪ struct元素匹配较为智能,比如range(min|max)不仅支持string也支持int类型
govalidator有一个bool类型的全局变量,可通过函数govalidator.SetFieldsRequiredByDefault()进行设置:
另外,valid tag里,可以通过显式设置方式更细颗粒度地控制:当遇到zero value时是需要验证还是提示错误。此设置可以覆盖SetFieldsRequiredByDefault()。所以,valid tag有如下几种写法
`valid:""` // 等同于空tag,即``
`valid:"-"`
`valid:","`
`valid:",optional`
`valid:",required`
接下来,分别测试:假设一个struct元素的值为空字符""(即zero value)
govalidator.SetFieldsRequiredByDefault(true)
`valid:""` // 报错:All fields are required to at least have one validation defined
`valid:"-"` // true
`valid:","` // 报错:Missing required field
`valid:",optional` // true
`valid:",required` // 报错:non zero value required
`valid:"ipv4"` // 报错:Missing required field
`valid:"ipv4,optional"` // true
`valid:"ipv4,required"` // 报错:non zero value required
`valid:""` // true
`valid:"-"` // true
`valid:","` // true
`valid:",optional` // true
`valid:",required` // non zero value required
`valid:"ipv4"` // true
`valid:"ipv4,optional"` // true
`valid:"ipv4,required"` // 报错:non zero value required
继续测试,当struct元素的值为不合法的ipv4地址字符串(非空字符串),如"192.168.1.1.1"
govalidator.SetFieldsRequiredByDefault(true)
`valid:""` // 报错:All fields are required to at least have one validation defined
`valid:"-"` // true
`valid:","` // true
`valid:",optional` // true
`valid:",required` // true
`valid:"ipv4"` // 报错:192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,optional"` // 报错:192.168.1.1.1 does not validate as ipv4
`valid:"ipv4,required"` // 报错:192.168.1.1.1 does not validate as ipv4
// 来自github
SetNilPtrAllowedByRequired causes validation to pass when struct fields marked by required are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between nil and zero value state can use this. If disabled, both nil and zero values cause validation errors.
// 来自godoc
SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. The validation will still reject ptr fields in their zero value state. Example with this enabled:
type exampleStruct struct {
Name *string `valid:"required"
With `Name` set to "", this will be considered invalid input and will cause a validation error. With `Name` set to nil, this will be considered valid by validation. By default this is disabled.
嵌套元素名必须是导出型,也就是大写字母开头,举例
package main
import (
"fmt"
"github.com/asaskevich/govalidator"
)
type Foo struct {
A string `valid:"ipv4"`
B string `valid:"mac"`
C int `valid:"range(0|100)"`
}
type bar struct {
X string `valid:"ipv4"`
Foo `valid:",required"`
}
func main() {
govalidator.SetFieldsRequiredByDefault(true)
b := bar{
X: "192.168.1.1",
}
b.Foo.A = "192.168.1.1.1"
b.Foo.B = "aa:bb:cc:dd:ee:ff"
b.Foo.C = 100
result, err := govalidator.ValidateStruct(b)
if err != nil {
fmt.Println("error: " + err.Error())
}
fmt.Println(result)
}
输出
error: Foo.A: 192.168.1.1.1 does not validate as ipv4;A: 192.168.1.1.1 does not validate as ipv4
false
注意:可以给Foo设置一个元素名,但也必须是大写字母开头,比如
MyFoo Foo `valid:",required"` // 正确,可以读取到
myFoo Foo `valid:",required"` // 错误,无法读取到
无法实现以嵌套为颗粒度的可选校验,比如下面这样是没有效果的
type bar struct {
X string `valid:"ipv4"`
Foo `valid:",optional"` // 不可行
}
因为上面代码实际会被转换为这样
type bar struct {
X string `valid:"ipv4"`
Foo.A string `valid:"ipv4"`
Foo.B string `valid:"mac"`
Foo.C int `valid:"range(0|100)"`
}
这就导致没有办法实现Foo全校验或者全不校验
建议全部显式配置校验,因为使用隐式一旦配置有误,难以及时发现
想做验证使用`valid:ipv4,required`
不想做验证使用`valid:",required"`
govalidator的校验功能还支持自定义tag与自定义校验函数,由于笔者尚未深度实践过,因此请参考官方github文档。
govalidator除了支持校验,还支持较为丰富的字符串裁剪、处理、正则等功能,以及若干类型转换功能,详见本文附录4、5(本文相比godoc和官网文档进行了更为细致的分类)。但笔者不推荐直接使用这些裁剪、处理、正则功能,因为实际上就是做了一层封装和一些细节处理,并不复杂,但可以学习。
笔者认为在使用govalidator的任何功能时,先看看源码,这是一个大而全的源码宝库,非常值得学习和借鉴。
参考文章