校验规则
完整的校验规则可参考https://godoc.org/github.com/go-playground/validator,下面分享常用的校验规则规则:
-
数字值限制
注:如果限制之间存在冲突,如
eq=10,ne=10
,则会根据先后顺序,后面的会覆盖前面的定义,以后面定义的为准;校验内容为eq=10,ne=10
,只会生效ne=10
,如果存在冲突,所有校验规则均符合此规律。- 限制范围
max=10 # 最大值为10,即小于等于10 min=10 # 最小值为10,即大于等于10 gt=10 # 大于 10 gte=10 # 大于等于10 lt=10 # 小于10 lte=10 # 小于等于10
- 限制值
eq=10 # 等于10 ne=10 # 不等于10 oneof=1 2 3 # 值只能是1、2 或 3 len=3 # 限制位数为3位数
- 限制范围
-
字符串长度限制
-
限制长度范围
max=10 # 最大长度为10 min=10 # 最小长度为10 gt=10 # 长度大于10 lt=10 # 长度小于10 gte=10 # 长度大于等于10 let=10 # 长度小于等于10
-
限制值内容
eq=aaa # 值为aaa ne=aaa # 值不能为aaa oneof=a b c # 枚举,只能为a、b 或 c len=3 # 字符长度为3
-
-
存在性校验
注:这里需要特别注意的是,这里是否存在是根据0值与非0值判断的,如果字段中传了对应的0值,也会被认为是不存在的
-
必选
required # 必须
-
可选
omitempty,xxx=xxx # 可选,如果存在,则继续向后校验规则xxx=xxx,如果不存在,则xxx=xxx不生效,但是如果omitempty之前存在校验规则,则前面的校验规则还是生效的,如 gte=-1,omitempty,len=3,则gte=-1规则始终生效,而len=3只有在值不为0时生效。
-
关联校验
required_with=AAA # 当AAA存在时,此字段也必须存在 required_with_all=AAA BBB CCC # 当AAA BBB CCC 都存在时,此字段必须存在 required_without=AAA # 当AAA不存在时,此字段必须存在 required_without_all=AAA BBB # 当AAA BBB 都不存在时,此字段必须存在
-
-
结构体字段间校验
-
一个层级内部校验
eqfield=AAA # 和字段AAA值相等 nefield=AAA # 和字段AAA值不相等 gtfield=AAA # 大于字段AAA的值 gtefield=AAA # 大于等于字段AAA的值 ltfield=AAA # 小于字段AAA的值 ltefield=AAA # 小于等于AAA字段的值
-
多个层级之间校验
eqcsfield=AAA.B # 等于AAA中的B字段 necsfield=AAA.B # 不等于AAA中的B字段 gtcsfield=AAA.B # 大于AAA中的B字段 gtecsfield=AAA.B # 大于等于AAA中的B字段 ltcsfield=AAA.B # 小于AAA中的B字段 ltecsfield=AAA.B # 小于等于AAA中的B字段
注:此处要注意的是,多个结构体之间的校验必须是字段之间存在层级联系,且只能是上层的与下层的做关联,不能反过来,如下:
type Timeout struct { Connect int `json:"connect" validate:"required"` Read int `json:"read" validate:"required"` Send int `json:"send" validate:"required"` } type MyStruct struct { Name string `json:"name" validate:"required"` Out int `json:"out" validate:"eqcsfield=Timeout.Connect"` // 只能是Timeout同层级的去关联Timeout下的字段,不能Timeout下的字段关联上层字段 Timeout Timeout `json:"timeout"` }
-
-
dive 对数组校验
dive 的意思是潜水,潜水也就是深入到水下,dive在这里的意思是也差不多,就是更深入一层的意思,如当前字段类型是
[]string
,那么深入一层,就从整体(array)
到局部(array中的一个元素)
,对应到校验上,就是dive前是对整体校验,dive后是对整体中的元素校验。示例:-
一般数组
Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
检验内容:
[]string
长度必须大于0,数组中元素string长度必须在1-100之间 -
结构体数组校验
Cors []AStruct `binding:"dive"`
虽然这里
dive
前后未定义相关校验规则,但是如果要启用 AStruct 中定义的校验规则,那么dive是必须的,否则AStruct
中定义的规则不会生效
-
-
dive对Map校验
dive
的含义在这里仍然类似于数组,整体 -> 局部
的规则不变,但是map的特殊性在于不仅有value值,还有key值,这里就需要方法去区分是校验value,还是校验key;这里使用了 keys 和 endkeys来标记key值的校验范围,从keys开始,至endkeys结束ReqHeaders map[string]string `binding:"dive,keys,min=1,max=100,endkeys,required,min=1,max=100"`
校验内容:未对整体做校验,限制key值长度必须在1-100之间,value值的长度也必须在1-100之间
-
structonly
当一个结构体定义了校验规则,但是在某些地方,不需要这些校验规则生效的时候,可以使用
structonly
标记,存在此标记的结构体内部的校验规则将不会再生效。如下:type Timeout struct { Connect int `json:"connect" binding:"required"` Read int `json:"read" binding:"required"` Send int `json:"send" binding:"required"` } type MyStruct struct { Name string `json:"name" binding:"required"` Timeout Timeout `json:"timeout" binding:"structonly"` }
结构体
Timeout
各个字段定义了校验内容,但是我在MyStruct
的Timeout
字段使用了structonly
标记,那么Timeout
中定义的校验内容将不再生效 -
nostructlevel
nostructlevel 类似于structonly,唯一不同之处是要想忽略规则的效果生效,必须要使用
required
或omitempty
type Timeout struct { Connect int `json:"connect" binding:"required,gte=1,lte=1000"` Read int `json:"read" binding:"gte=1,lte=1000"` Send int `json:"send" binding:"gte=1,lte=1000"` } type MyStruct struct { Name string `form:"name" json:"name" binding:"required"` UseMeta bool `json:"use_meta"` Timeout Timeout `json:"timeout" binding:"required,nostructlevel"` }
-
忽略校验规则
- # 忽略字段校验规则,常用于嵌套结构中,用来忽略嵌套结构下的校验规则
-
多校验规则
或
运算| # 多个检验规则默认之间是与关系,使用 | 可实现多运算规则之间进行或运算
gin中的使用方法
1. 校验参数定义
校验参数是定义在结构体上的,因此在首先需要定义好接受数据的结构体,再定义每个属性的校验参数:
type Timeout struct {
Connect int `binding:"omitempty,gte=1,lte=75"`
Read int `binding:"omitempty,gte=1,lte=1000"`
Send int `binding:"omitempty,gte=1,lte=1000"`
}
type MyTestStruct struct {
Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
Name string `binding:"required"`
UseTimeout bool
Timeout Timeout `binding:"required_with=UseTimeout"`
}
结构体字段 binding
tag的内容就是校验参数。
2. 数据绑定
gin中提供了常用的 ShouldBindWith/ShouldBindUri
方法,来实现自动的数据与结构体的绑定,提供了以下类型的数据:
数据类型 | Tag | Context-Type | 数据源 | 使用示例 |
---|---|---|---|---|
JSON | json | application/json | Body | ShouldBindWith(&data, binding.Query) |
XML | xml | application/xml、text/xml | Body | ShouldBindWith(&data, binding.XML) |
Form | form | application/x-www-form-urlencoded | Body | c.ShouldBindWith(&data, binding.Form) |
Query | form | url query | ShouldBindWith(&data, binding.Query) | |
FormPost | form | application/x-www-form-urlencoded | Body | ShouldBindWith(&data, binding.FormPost) |
FormMultipart | form | multipart/form-data | Body | ShouldBindWith(&data, binding.FormMultipart) |
ProtoBuf | application/x-protobuf | Body | ShouldBindWith(&data, binding.ProtoBuf) | |
MsgPack | application/x-msgpack、application/msgpack | Body | ShouldBindWith(&data, binding.MsgPack) | |
YAML | application/x-yaml | Body | ShouldBindWith(&data, binding.YAML) | |
Uri | uri | uri | ShouldBindUri(&data) | |
Header | header | Header | ShouldBindWith(&forwardRule, binding.Header) |
说明:
-
数据类型:指的是传输的数据类型
-
Tag:结构体定义的Tag,揭示了数据字段与结构体字段的对应关系,如:Form类型数据中
id
对应结构体中的ID
字段,那么就需要定义一下内容:type xxxx struct { ... ID int64 `form:"id"` ... }
-
Context-Type:HTTP中的Header 字段之一,表示Body的数据类型,
gin.Context
中的Bind
方法会根据Content-Type
自动绑定数据到对应类型上func (c *Context) Bind(obj interface{}) error { b := binding.Default(c.Request.Method, c.ContentType()) // 获取数据类型 return c.MustBindWith(obj, b) // 校验数据 }
注:
MustBindWith
与ShouldBindWith
的区别是MustBindWith
会自动返回错误信息,如果你有统一的错误返回格式,MustBindWith
可能会破坏返回格式的统一性。 -
数据源:指的是从哪里取数据,
Body
指的是从HTTP Body中获取数据,url query
指从URL的quey参数中获取数据,url
指的是从url中获取参数,如:/api/v1/xxx/:id
,id参数就是从url中来获取的 -
使用示例:使用的例程
3. 校验结果获取
ShouldBindWith
会返回error信息,如果error信息不为空,则表明出现错误,错误信息就包含在error中,如果error为空,则表明校验通过,示例如下:
var data MyTestStruct // 存储数据内容
// 获取 & 校验数据
if err = c.ShouldBindWith(&data, binding.JSON); err != nil {
// 出现错误,打印错误内容
fmt.Println(err)
return
}
// 校验成功,打印数据内容
fmt.Println(data)