【Go】参数验证,对象数组核验-validator
创始人
2024-04-27 07:35:05
0

文章目录

  • 背景
  • 功能介绍
    • 范围比较验证
    • 标记之间特殊符号说明
    • 字符串验证
    • 特殊字符串验证
    • 例子
  • 扩展问题
    • 我的问题
      • 验证slice
        • 举例
        • 输出
        • 说明
        • 详细举例
      • 二维slice
        • 举例:
        • 说明
      • map核验
        • 举例
        • 输出
        • 说明
      • 嵌套map核验
        • 举例
        • 说明
  • 参考

背景

一直做的 go 项目中想要方便简洁的对接口参数字段进行核验,选择与 gin 框架适配的validator库作为验证组件,基础用法都比较简单,对字符数字型值校验,对切片、数组、map 类型的范围检验,基础使用起来都比较简单易懂好上手,但近期突然遇到一个意外错误,本来该校验的对象数组,却没有按照我预计的核验这个数组里每个对象所属字段的值是否符合要求,由此记录一下。

功能介绍

范围比较验证

范围验证: 切片、数组和map、字符串,验证其长度;数值,验证大小范围

  • lte:小于等于参数值,validate:“lte=3” (小于等于3)
  • gte:大于等于参数值,validate:“lte=120,gte=0” (大于等于0小于等于120)
  • lt:小于参数值,validate:“lt=3” (小于3)
  • gt:大于参数值,validate:“lt=120,gt=0” (大于0小于120)
  • len:等于参数值,validate:“len=2”,字符串长度必须为n,或者是数组、切片、map的len的值
  • max:最大值,小于等于参数值,validate:“max=20” (小于等于20)
  • min:最小值,大于等于参数值,validate:“min=2,max=20” (大于等于2小于等于20)
  • ne:不等于,validate:“ne=2” (不等于2)
  • oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,validate:“oneof=red green”

标记之间特殊符号说明

  • 逗号( ,):把多个验证标记隔开。注意:隔开逗号之间不能有空格, validate:“lt=0,gt=100”,逗号那里不能有空格,否则panic
  • 横线( - ):跳过该字段不验证
  • 竖线( | ):使用多个验证标记,但是只需满足其中一个即可
  • required:表示该字段值必输设置,且不能为默认值
  • omitempty:如果字段未设置,则忽略它

字符串验证

  • contains:包含参数子串,validate:“contains=tom” (字段的字符串值包含tom)
  • excludes:包含参数子串,validate:“excludes=tom” (字段的字符串值不包含tom)
  • startswith:以参数子串为前缀,validate:“startswith=golang”
  • endswith:以参数子串为后缀,validate:“startswith=world”

特殊字符串验证

  • email:验证字符串是email格式。默认为必填
  • url:验证字符串是URL格式。默认为必填
  • uri:字段值是否包含有效的uri,validate:“uri”
  • ip:字段值是否包含有效的IP地址,validate:“ip”
  • ipv4:字段值是否包含有效的ipv4地址,validate:“ipv4”
  • ipv6:字段值是否包含有效的ipv6地址,validate:“ipv6”

例子

简单举例,可自行测试

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
}

扩展问题

我的问题

验证slice

因为项目的接口参数的结构体是复杂,往往嵌套着对象数组或切片,不是简单由一个结构体就可以概括接收,在项目后续突然发现一个本该检验的对象数组里的某个参数,因对方方也疏忽未传值,导致后面检验出现问题,排查之前虽然问题不在我这边,但的却这个校验字段没有生效,后续排查了一下对于数组或切片或 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"使用

二维slice

当然二维切片也差不多原理

举例:

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核验

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核验

如: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代码地址

相关内容

热门资讯

安卓双系统添加应用,轻松实现多... 你有没有想过,你的安卓手机里可以同时运行两个系统呢?听起来是不是很酷?想象一边是熟悉的安卓系统,一边...
pipo安卓进系统慢,探究pi... 最近是不是发现你的Pipo安卓系统更新或者运行起来特别慢?别急,今天就来给你好好分析分析这个问题,让...
怎样使用安卓手机系统,安卓手机... 你有没有发现,安卓手机已经成为我们生活中不可或缺的一部分呢?从早晨闹钟响起,到晚上睡前刷剧,安卓手机...
双系统安卓安装caj,轻松实现... 你有没有想过,你的安卓手机里装上双系统,是不是就能同时享受安卓和Windows系统的乐趣呢?没错,这...
安卓使用ios系统教程,安卓用... 你是不是也和我一样,对安卓手机上的iOS系统充满了好奇?想要体验一下苹果的优雅和流畅?别急,今天我就...
安卓系统gps快速定位,畅享便... 你有没有遇到过这样的情况:手机里装了各种地图导航软件,但每次出门前都要等上好几分钟才能定位成功,急得...
安卓手机系统更新原理,原理与流... 你有没有发现,你的安卓手机最近是不是总在提醒你更新系统呢?别急,别急,让我来给你揭秘一下安卓手机系统...
安卓系统通知管理,全面解析与优... 你有没有发现,手机里的通知就像是一群调皮的小精灵,时不时地跳出来和你互动?没错,说的就是安卓系统的通...
安卓系统手机哪买,揭秘哪里购买... 你有没有想过,拥有一部安卓系统手机是多么酷的事情呢?想象你可以自由安装各种应用,不受限制地探索各种功...
安卓系统 ipv4,基于安卓系... 你知道吗?在智能手机的世界里,有一个系统可是无人不知、无人不晓,那就是安卓系统。而在这个庞大的安卓家...
目前安卓是什么系统,探索安卓系... 亲爱的读者,你是否曾好奇过,如今安卓系统究竟是什么模样?在这个科技飞速发展的时代,操作系统如同人体的...
安卓6.0系统比5.0,从5.... 你有没有发现,自从手机更新了安卓6.0系统,感觉整个人都清爽了不少呢?没错,今天咱们就来聊聊这个话题...
安卓2.36系统升级,功能革新... 你知道吗?最近安卓系统又来了一次大变身,那就是安卓2.36系统升级!这可不是一个小打小闹的更新,而是...
安卓系统源码怎么打开,并可能需... 你有没有想过,安卓系统的源码就像是一扇神秘的门,隐藏着无数的技术秘密?想要打开这扇门,你得掌握一些小...
安卓8.0系统体验视频,智能革... 你有没有听说安卓8.0系统最近可是火得一塌糊涂啊!作为一个紧跟科技潮流的数码达人,我当然要来给你好好...
宣传系统漫画app安卓,探索安... 亲爱的读者们,你是否曾在某个午后,百无聊赖地打开手机,想要寻找一些轻松愉悦的读物?今天,我要给你介绍...
鸿蒙替换安卓系统吗,开启智能生... 你知道吗?最近科技圈里可是炸开了锅,因为华为的新操作系统鸿蒙系统,据说要大举进军手机市场,替换掉安卓...
手机安卓系统深度清理,解锁手机... 手机里的东西是不是越来越多,感觉就像一个装满了杂物的储物柜?别急,今天就来教你一招——手机安卓系统深...
安卓上的windows系统,融... 你有没有想过,在安卓手机上也能体验到Windows系统的魅力呢?没错,这就是今天我要跟你分享的神奇故...
安卓系统焦点变化事件,Andr... 你知道吗?在安卓系统的世界里,最近发生了一件超级有趣的事情——焦点变化事件。这可不是什么小打小闹,它...