一直做的 go 项目中想要方便简洁的对接口参数字段进行核验,选择与 gin 框架适配的validator库作为验证组件,基础用法都比较简单,对字符数字型值校验,对切片、数组、map 类型的范围检验,基础使用起来都比较简单易懂好上手,但近期突然遇到一个意外错误,本来该校验的对象数组,却没有按照我预计的核验这个数组里每个对象所属字段的值是否符合要求,由此记录一下。
范围验证: 切片、数组和map、字符串,验证其长度;数值,验证大小范围
简单举例,可自行测试
type UserInfo struct {ID int `validate:"gt=0"`Age int `validate:"gt=0"`Name string `validate:"required"`Sex string `validate:"required"`
}
func InitUserInfo(id, age int, name, sex string) *UserInfo {// new一个校验器valid := validator.New()// 初始化UserInfouserInfo := &UserInfo{ID: id,Age: age,Name: name,Sex: sex,}if err := valid.Struct(userInfo); err != nil {fmt.Println("参数校验不通过", err)}return userInfo
}
func TestValidate(t *testing.T) {InitUserInfo(1, 2, "kevin", "男") // 参数校验通过InitUserInfo(0, 2, "kevin", "男") // 参数校验不通过 Key: 'UserInfo.ID' Error:Field validation for 'ID' failed on the 'gt' tagInitUserInfo(1, 2, "kevin", "") // 参数校验不通过 Key: 'UserInfo.Sex' Error:Field validation for 'Sex' failed on the 'required' tag
}
因为项目的接口参数的结构体是复杂,往往嵌套着对象数组或切片,不是简单由一个结构体就可以概括接收,在项目后续突然发现一个本该检验的对象数组里的某个参数,因对方方也疏忽未传值,导致后面检验出现问题,排查之前虽然问题不在我这边,但的却这个校验字段没有生效,后续排查了一下对于数组或切片或 Map 类型的检验需要使用 dive 才能核验到下一层级的字段里。
sliceone := []string{"123", "onetwothree", "myslicetest", "four", "five"}
validate.Var(sliceone, "max=15,dive,min=4")
Key: '[0]' Error:Field validation for '[0]' failed on the 'min' tag
Key: '' Error:Field validation for '' failed on the 'min' tag
第二个参数中tag关键字 dive 前面的 max=15,验证 [] , 也就是验证slice的长度,
dive 后面的 min=4,验证slice里的值长度,也就是说 dive 后面的 tag 验证 slice 的值
结合上面简单使用的例子使用 dive 核验切片数组对象字段实现
import ("fmt""github.com/go-playground/validator/v10""testing"
)type UserInfo struct {ID int `validate:"gt=0"`Age int `validate:"gt=0"`Name string `validate:"required"`Sex string `validate:"required"`
}type Teacher struct {Member []*UserInfo `validate:"max=15,dive,required"`
}func InitUserInfo(id, age int, name, sex string) *UserInfo {// new一个校验器valid := validator.New()// 初始化UserInfouserInfo := &UserInfo{ID: id,Age: age,Name: name,Sex: sex,}var menber []*UserInfomenber = append(menber, userInfo)teacher := &Teacher{Member: menber,}if err := valid.Struct(teacher); err != nil {fmt.Println("参数校验不通过", err)}return userInfo
}func TestValidate(t *testing.T) {InitUserInfo(1, 2, "kevin", "男") // 参数校验通过InitUserInfo(0, 2, "kevin", "男") // 参数校验不通过 Key: 'UserInfo.ID' Error:Field validation for 'ID' failed on the 'gt' tagInitUserInfo(1, 2, "kevin", "") // 参数校验不通过 Key: 'UserInfo.Sex' Error:Field validation for 'Sex' failed on the 'required' tag
}
当教师结构体中的参数内包含用户信息结构体,需要核验用户信息的字段时候就需要加上validate:"max=15,dive,required"
,这样就很核验用户信息结构体字段是否也符合要求
输出如下
也可不加前面的长度,直接validate:"dive,required"
使用
当然二维切片也差不多原理
slicethree := [][]string{} validate.Var(slicethree, "min=2,dive,len=2,dive,required")
validate.Var(slicethree, "min=2,dive,dive,required")
这里有2个 dive,刚好深入到二维slice,但他们也有不同之处,第二个表达式的第一个dive后没有设置tag。
第一个验证表达式:
min=2:验证第一个 [] 方括号的值长度 ;
len=2:验证第二个 []string 长度;
required:验证slice里的值
第二个验证表达式:
min=2:验证第一个 [] 方括号的值长度 ;
dive: 后没有设置tag值,不验证第二个 []string ;
required: 验证slice里的值
map的验证中也需要tag关键字 dive, 另外,它还有 keys 和 endkeys 两tag,验证这2个tag之间map的 key,而不是value值。
var mapone map[string]string mapone = map[string]string{"one": "jimmmy", "two": "tom", "three": ""}validate := validator.New() err := validate.Var(mapone, "gte=3,dive,keys,eq=1|eq=2,endkeys,required")
Key: '[three]' Error:Field validation for '[three]' failed on the 'eq=1|eq=3' tag
Key: '[three]' Error:Field validation for '[three]' failed on the 'required' tag
Key: '[one]' Error:Field validation for '[one]' failed on the 'eq=1|eq=3' tag
Key: '[two]' Error:Field validation for '[two]' failed on the 'eq=1|eq=3' tag
gte=3:验证map自己的长度;
dive后的 keys,eq=1|eq=2,endkeys:验证map的keys个数,也就是验证 [] 里值。上例中定义了一个string,所以明显报了3个错误。
required:验证 map的值value
如:map[[3]string]string,和上面slice差不多,使用多个 dive
var maptwo map[[3]string]string{} validate.Var(maptwo, "gte=3,dive,keys,dive,eq=1|eq=3,endkeys,required")
gte=3: 验证map的长度;
keys,dive,eq=1|eq=3,endkeys:keys和endkeys中有一个dive(深入一级),验证map中key的数组每一个值
required: 验证map的值
golang常用库:字段参数验证库-validator使用
validator代码地址