先介绍下validator
validator v8与v9
gin中使用的validator是v8版本,v8与v9版本区别比较大。
其中一个区别是v8版本没有指定默认的TagName,需要在每次声明validator实例的时候配置;在v9中设置了默认的TagName就是validate。我们的gin框架在使用的时候设置的TagName为”binding”。
validator中给的例子代码
1 | var validate *validator.Validate |
gin中设置的TagName,文件gin/binding/default_validator.go
1 | func (v *defaultValidator) lazyinit() { |
这也就是为啥gin框架里面example中的定义的struct都是binding开头
1 | type Booking struct { |
其他区别例如实例化不同等
validator使用的tag
validator使用的struct的tag有以下这么多,其中有一些是通过正则表达式实现类型判断的,例如email、rgb、uuid等;有一些是通过第三方库实现的,例如ipv4、cidr等。
1 | // validator/baked_in.go |
tag中出现的dive的使用,dive一般用在slice、array、map、嵌套的struct验证中,作为分隔符表示进入里面一层的验证规则
1 | type Test struct { |
required表示字段必须有值,并且不为默认值,例如bool默认值为false、string默认值为””、int默认值为0。如果有些字段是可填的,并且需要满足某些规则的,那么需要使用omitempty;
1 | type Test struct { |
oneof自定义枚举值
1 | type Test struct { |
字段之间的关系使用如下标签,例如gtfield=CheckIn代表字段值要大于CheckIn字段的值。
1 | "eqfield": isEqField, |
自定义的验证tag的使用例子,以下自定义了is-awesome标签。
1 | package main |
自定义struct level的验证,以下例子在验证FirstName与LastName时候,为了确保他们之中必须有一个有值。
同样以下例子同时使用了嵌套的struct,在User中定义了一个指向Address的列表指针,该指针不能为nil,并且指针指向的Address值也能为nil。
1 | package main |
gin是如何使用validator的?
- gin中的struct的tag增加了content-type支持,分别是一下所示,其中ProtoBuf 还不支持验证常用的就form、query、json和xml;经过测试url中的传参和body中传参(application/x-www-form-urlencoded)都可以使用form标签
1
2
3
4
5
6
7
8
9
10var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
)1
2
3
4type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
} - 还支持添加字段的默认值
1
2
3
4type Login struct {
User string `form:"user,default=nidaye" binding:"required"`
Password string `form:"password" binding:"required"`
} - shouldbind与mustbind的区别,这里直接列出源代码解释。
1
2
3
4
5
6
7
8
9// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error ocurrs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
if err = c.ShouldBindWith(obj, b); err != nil {
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
}
return
} - 问gin中是如何把定义的Struct结合到浏览器传过来的数据,然后交给validator校验的呢?
以query类型为例子,文件gin/binding/query.go
;在bind方法中通过mapForm方法绑定值到sturct类型上,具体怎么绑可以看源码,大概就是利用反射。1
2
3
4
5
6
7func (queryBinding) Bind(req *http.Request, obj interface{}) error {
values := req.URL.Query()
if err := mapForm(obj, values); err != nil {
return err
}
return validate(obj)
}