博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
仿Gin搭建自己的web框架(六)
阅读量:5732 次
发布时间:2019-06-18

本文共 3284 字,大约阅读时间需要 10 分钟。

这一篇介绍gin中的绑定和数据验证。

不管是在query中,还是在body中,如果要一个一个的去获取参数并放入对应的变量中,是一个比较繁琐的过程,gin里边提供了一个自动绑定的方法,能够将query或者body中的参数方便的放入到我们定义的struct中。

同时在绑定参数的时候,我们也能够指定参数的范围或者特性,对参数进行验证。

所以将绑定和参数验证放在一块讲,接下去实现一个比较简单的绑定功能和参数验证。

参数绑定

func (c *Context) EnsureBody(item interface{}) bool {	if err := c.ParseBody(item); err != nil {		c.Fail(400, err)		return false	}	return true}// Parses the body content as a JSON input. It decodes the json payload into the struct specified as a pointer.func (c *Context) ParseBody(item interface{}) error {	decoder := json.NewDecoder(c.Req.Body)	if err := decoder.Decode(&item); err == nil {		return Validate(c, item)	} else {		return err	}}复制代码

我们用一个EnsureBody的函数来解析请求包中的body,它其实是调用了一个ParseBody的函数。这个函数会将req.Body解析到item中,如果解析失败,说明参数不符合我们的要求,返回错误。如果解析成功,就是将参数绑定到了我们定义的结构体中,接下去调用Validate对参数的有效性进行验证。

参数验证

首先说一下validate参数验证的好处。假设一个最简单的场景, 用户登录的时候必须要传账户名和密码,我们先定义这样的结构体

// LoginJSON .type LoginJSON struct {	User     string `json:"user"`	Password string `json:"password"`}复制代码

在从请求中接受参数之后,必然需要用if语句去判断参数中是否确实带了User参数和Password参数,这个在单一的请求中还算好办,两条if语句就解决了。但是如果有很多类似的请求,要验证其他的结构体,或者结构体中有很多的参数需要验证,这个就需要一堆的if语句,在代码简洁性上就会大打折扣。

但是如果有了validate模块,就能够解放劳动力,我们只需要把参数的要求在tag中写清楚,具体的验证过程全都可以交给validate模块去做。比如登录模块User和Password必须携带,就可以这么定义结构体

// LoginJSON .type LoginJSON struct {   User     string `json:"user" binding:"required"`   Password string `json:"password" binding:"required"`}复制代码

用一个required来限定必须携带的参数,再比如注册的例子,在gin中可以这么定义结构体

type RegisterReq struct {    // 字符串的 gt=0 表示长度必须 > 0,gt = greater than    Username       string   `validate:"gt=0"`    // 同上    PasswordNew    string   `validate:"gt=0"`    // eqfield 跨字段相等校验    PasswordRepeat string   `validate:"eqfield=PasswordNew"`}复制代码

注册的时候限定了UserName的长度和Password的长度,并且两次数据的密码必须一致。我们只需要关注参数的定义逻辑就可以,无需关注请求中的参数验证逻辑,validate都帮我们做了。

当然这个需要很复杂的validate模块,这边只是简单的实现validate中required的功能。

从结构上来说,如果我们自定义的struct中子struct,那么它其实有点类似一棵树的结构,在验证的时候就需要对整棵树进行验证,这个就涉及到树的遍历。遍历一棵树无非就是广度优先或者深度优先,在gin中采用了深度优先的方式。

for i := 0; i < typ.NumField(); i++ {		field := typ.Field(i)		fieldValue := val.Field(i).Interface()		zero := reflect.Zero(field.Type).Interface()		// Validate nested and embedded structs (if pointer, only do so if not nil)		if field.Type.Kind() == reflect.Struct ||			(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {			err = Validate(c, fieldValue)		}		if strings.Index(field.Tag.Get("binding"), "required") > -1 {			if reflect.DeepEqual(zero, fieldValue) {				name := field.Name				if j := field.Tag.Get("json"); j != "" {					name = j				} else if f := field.Tag.Get("form"); f != "" {					name = f				}				err = errors.New("Required " + name)				c.Error(err, "json validation")			}		}	}复制代码

在validate模块中,循环遍历所有的字段,如果字段是一个指针指向另一个结构体,那么采用深度优先的方法,验证这个子结构体的合法性。如果参数的tag中,带有required限制,验证这个参数是否确实有值。

main函数修改

在上一篇的基础之上,为了验证参数绑定功能,增加一个账号密码登录功能

v1 := r.Group("/v1"){	v1.POST("/pwlogin", v1PasswordLoginfunc)}复制代码

对应的handler如下

func v1PasswordLoginfunc(c *gin.Context) {	var json LoginJSON	if c.EnsureBody(&json) {		if json.User == "harleylau" && json.Password == "password" {			c.JSON(200, gin.H{
"status": "you are logged in"}) } else { c.JSON(401, gin.H{
"status": "unauthorized"}) } }}复制代码

将参数解析验证之后,在handler中只需要关注账号密码是否正确就可以,无需关注于参数错误的处理过程。

对应的代码参见:

转载于:https://juejin.im/post/5c9ae286f265da60d0004dc3

你可能感兴趣的文章
【Java】获取一个目录下的名称符合一定要求的全部文件+目录
查看>>
锤子OneStep及BigBang使用体验
查看>>
CSS z-index 属性
查看>>
AgileEAS.NET平台工具之数据原型设计器的使用场景示例
查看>>
libyuv编译【转】
查看>>
【下载】(.NET)JTBC网站内容管理系统(v2.0.0.9)
查看>>
SQLServer之删除触发器
查看>>
RxJava 学习介绍
查看>>
程序员你在干嘛?——我在改变世界
查看>>
05-码蚁JavaWeb之Tomcat插件安装
查看>>
XMind——让你的思维导图更加精彩纷呈!
查看>>
一个效果很棒的搜索框,跟srollview更配---从EMUI有感而写
查看>>
Java中的设计模式
查看>>
TCP UDP包大小分析
查看>>
iOS开发—AVFoundation实现动态人脸识别
查看>>
看了那么多,TCP/IP究竟是什么(一)
查看>>
如何智能化改造工单系统分发
查看>>
[译] 如果只有一个月入门 iOS:我该如何学习呢?
查看>>
原生函数----《你不知道的js》
查看>>
React小知识(3) - 国际化中碰到的问题
查看>>