在我们的实际业务场景中,经常会有这样的情况出现:要对接口struct字段进行参数验证。比如:
- Uid的值在某一个范围内
- Name值的长度在某一个范围内
- Sex的值符合男或女
- Email格式正确等等
其实在godoc里可以搜到很多第三方数据校验的模块,今天给大家推荐的是 asaskevich/govalidator,原因如下:
- star最多、持续更新发布
- 功能完善、使用便利
- 丰富的字符串校验、数据匹配、裁剪拼接处理等
- 支持struct元素合法性校验,并且支持嵌套检查
- 源码也值得一读,值得学习,就是一个百宝箱
这个项目的地址是: https://github.com/asaskevich/govalidator,详细的文档地址是:https://godoc.org/github.com/asaskevich/govalidator。
通过下面的方式就可以“安装” asaskevich/govalidator ,然后在需要使用的地方导入即可。如果你使用了Go module来管理第三方包,那么你可以参考 使用了Go mod管理的项目里如何添加新的第三方包? 来详细了解如何导入一个新的第三方包到本地项目中。
通过上面的一顿操作,恭喜你已经可以尽情体验 govalidator 中的所有功能了!
今天,我们就围绕 asaskevich/govalidator来介绍如何完成“各种各样”的参数验证。
一、最简单的字符串匹配
govalidator的使用方法非常简单,只需要import导入这个包即可,然后就可以调用相应的方法进行参数校验了。
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
二、struct元素匹配
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元素只支持部分常用的校验,详细可以参考官方文档:https://github.com/asaskevich/govalidator#validatestruct-2
- struct元素必须是导出型,也就是必须大写字母开头,govalidator才会去做校验
- struct元素匹配较为智能,比如range(min|max)不仅支持string也支持int类型
三、struct元素可选验证
govalidator有一个bool类型的全局变量,可通过函数 govalidator.SetFieldsRequiredByDefault()进行设置,通常情况下,在init函数或者main中进行初始化。
- true时,如果没有定义valid tag,则会提示错误
//假设一个struct元素的值为空字符""(即zero value)情况下的表现
`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
//假设一个struct元素的值为空字符""(即zero value)情况下的表现
`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
另外在valid tag里,可以通过显式设置方式更细颗粒度地控制:当遇到“zero value”时是需要验证还是提示错误。此设置会覆盖调上面这个全局变量的设置。
`valid:""` // 等同于空tag,即``
`valid:"-"` //可免除,即可选的,不是必须字段
`valid:","`
`valid:",optional` //optional可选的,不是必须字段
`valid:",required`
还有一个全局变量nilPtrAllowedByRequired,通过govalidator.SetNilPtrAllowedByRequired()设置。这个全局变量默认是false,当由required标记的struct字段设置为nil时,SetNilPtrAllowedByRequired会导致验证通过。
package main
import (
"fmt"
"github.com/asaskevich/govalidator"
)
type myStruct struct {
A []string `valid:",required"`
B int
}
func main() {
var a myStruct
x,err := govalidator.ValidateStruct(a)
if err != nil {
fmt.Println("error: " + err.Error())
}
fmt.Println(x)
}
上面的情况下会输出错误:error: A: non zero value required
四、struct元素嵌套校验
嵌套元素名必须是导出型,也就是大写字母开头。
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
关于参数校验的内容今天就讨论到这里,更多深入的研究大家可以参考官方文档或者阅读下源码,会对大家非常有帮助。