内容简介:在第二节,我们学习了Gin框架的路由定义与参数接收,今天应一位同学的要求,来讲解一下参数的绑定与校验。本不必抛出这个问题的,但顾及到初出茅庐的同学,这里解释一下。
导读
在第二节,我们学习了Gin框架的路由定义与参数接收,今天应一位同学的要求,来讲解一下参数的绑定与校验。
为什么校验参数?
本不必抛出这个问题的,但顾及到初出茅庐的同学,这里解释一下。
假设做一个注册接口,传过来的用户名是不是不能太骚气?比如一堆空格和符号之类的;密码是不是不能太长也不能太短?手机号是不是要符合规则?性别是不是不能填人妖?
另外,登录的时候我们也需要验证账号密码是不是正确的,那么为了方便上手,咱就先来个简单示例,做登录验证。
激情演示
做登录之前得先想清楚需要对用户名密码做什么样的限制,比如他们都不能为空、用户名只能是字母或数字、密码长度只能在6位到12位之间等,如果各位看官没有异议,接下来我就拿上述的这几个条件来演示了。
定义结构体与接口
首先得有个地方存接收到的用户名、密码参数,那就定一个名叫 Login
的结构体吧。
type Login struct { User string Password string }
然后再定义一个登录接口,这块不知道啥意思的同学可以去看我的第二篇教程。
router.POST("/login", func(c *gin.Context) { })
接口是定了,怎么才能把接收的参数放到 Login
结构体里去呢?
绑定参数
我们去翻一下 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) }
哇塞,这就有意思了,调用一下试试。记得把 Login
的引用传过去,毕竟人家还要赋值的。
router.POST("/login", func(c *gin.Context) { var login Login c.Bind(&login) c.JSON(200, login) })
果不其然,跑起来的同学应该可以发现,它失败了,并没有取到我想要的参数值。
curl -d "user=pingye&password=123" http://localhost:8080/login {"User":"","Password":""}
这不对啊,不是说好做彼此的天使吗?
不行,我要一层一层剥开 gin
框架是怎么处理的。
绑定失败,剖析源码
经过长达60分钟的精心研究,我终于发现了终极奥义,先把我绘制的图贴上。
事情是这样的,我们调用的 Bind
方法实际调用了两个方法 binding.Default
和 c.MustBindWith
,前者的主要作用是根据终端请求的 Content-Type
选择处理器,没办法,gin太强,支持的类型太多了。我们刚才的请求方式被理所应当的分配给了formBinding。
var ( JSON = jsonBinding{} XML = xmlBinding{} Form = formBinding{} Query = queryBinding{} FormPost = formPostBinding{} FormMultipart = formMultipartBinding{} ProtoBuf = protobufBinding{} MsgPack = msgpackBinding{} YAML = yamlBinding{} Uri = uriBinding{} Header = headerBinding{} )
然后呢?在拿到了formBinding之后,就来到了c.MustBindWith方法,它的作用就是调用formBinding的Bind方法,原来这哥们就是个中间商在赚差价。
func (formBinding) Bind(req *http.Request, obj interface{}) error { if err := req.ParseForm(); err != nil { return err } if err := req.ParseMultipartForm(defaultMemory); err != nil { if err != http.ErrNotMultipart { return err } } if err := mapForm(obj, req.Form); err != nil { return err } return validate(obj) }
Bind方法主要就干了两件事情,第一是解析传过来的表单参数,第二是找到结构体里的tag form
进行匹配赋值。看到这里我就明白了,原来只需要在 Login
后面加上一个tag。
继续绑定参数
那我们加上tag试一下。
type Login struct { User string `form:"user"` Password string `form:"password"` }
跑起来果然很完美。
curl -d "user=pingye&password=123" http://localhost:8080/login {"User":"pingye","Password":"123"}
看到了这里,聪明的你应该涌出了很多想法,刚才说支持那么多类型,前端传的是json咋搞呢?同学们可以自己试一下,现有的这套代码啥都不用改就可以解析json,因为jsonBinding并没有去Login结构体找tag,所以不用在后面加上 json:"user"
的标识。
curl -H "Content-Type:application/json" -d '{"user":"pingye","password":"123455"}' http://localhost:8080/login {"User":"pingye","Password":"123455"}
至于其他的类型,同学们可以自己去动手试验一下,我们必须得到参数验证环节了。
参数验证
OK,这就到了激动人心的参数验证时刻了,再回顾一下刚才的需求,用户名和密码不能为空,用户名只能是英文和数字,密码长度必须得在6到12位。
gin官方给出的示例是直接在tag中加校验规则,比如不能为空,就加上 binding:"required"
。
type Login struct { User string `form:"user" binding:"required"` Password string `form:"password" binding:"required"` }
在验证的地方也做一下处理,Bind会自动帮我们进行校验,如果校验失败会返回一个error,我们把它输出即可。
router.POST("/login", func(c *gin.Context) { var login Login err := c.Bind(&login) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(200, login) })
放心,参数验证的演示不会就这么结束的。
鉴于官方文档给的信息太少了,我们还是通过源码去找更多线索吧,通过源代码可以看到,gin的参数验证实际上并不是自己实现的,而是使用了一个叫做 go-playground/validator
的库。
. ├── LICENSE ├── Makefile ├── README.md ├── _examples ├── baked_in.go ├── benchmarks_test.go ├── cache.go ├── doc.go ├── errors.go ├── field_level.go ├── go.mod ├── go.sum ├── logo.png ├── non-standard ├── regexes.go ├── struct_level.go ├── testdata ├── translations ├── translations.go ├── util.go ├── validator.go ├── validator_instance.go └── validator_test.go 4 directories, 19 files
里面有一个叫做 doc.go
的文件,有非常多的示例与解释,简直找到宝藏了,去他的官方文档。
我在里面找到了长度限制的demo,很简单,min和max两个标签就搞定了。跑一下完全没有问题。
type Login struct { User string `form:"user" binding:"required"` Password string `form:"password" binding:"required,min=6,max=12"` }
curl -d "user=pingye&password=12345" http://localhost:8080/login {"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'min' tag"} curl -d "user=pingye&password=1234567890123" http://localhost:8080/login {"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'max' tag"}
实现了两个校验了,还剩下用户名的字母和数字限制,让我震惊的是,我以为随便说的这个限制要用多种组合来实现,竟然轻松就找到了一个对应的,简直太棒了(强烈推荐这个库,看来后面有必要出一期这个库的介绍),很简单,加上一个名叫 alphanum
的规则就可以实现了。
type Login struct { User string `form:"user" binding:"required,alphanum"` Password string `form:"password" binding:"required,min=6,max=12"` }
今天我的任务结束了,各位是不是需要查看源码,来吧来吧, 点击查看源码 ,顺便STAR一下我哈。
Go语言库示例开源项目「 golang-examples 」欢迎 star~
https://github.com/pingyeaa/golang-examples
感谢大家的观看,如果觉得文章对你有所帮助,欢迎关注公众号「平也」,聚焦 Go 语言与技术原理。
以上所述就是小编给大家介绍的《Gin框架系列04:趣谈参数绑定与校验》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 云计算底层技术之高性能集群趣谈
- vue实战 - 车牌号校验和银行校验
- 更加灵活的参数校验,Spring-boot自定义参数校验注解
- 一坨一坨的 if/else 参数校验,终于被 Spring Boot 参数校验组件整干净了
- SpringMVC——数据校验
- gin请求数据校验
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web开发秘方
Brian P. Hogan、Chris Warren、Mike Weber、Chris Johnson、Aaron Godin / 七印部落 / 华中科技大学出版社 / 2013-7-10 / 66.00元
猜猜硅谷的前端工程师怎么折腾JS的?想知道无限下拉的列表怎么做吗?你知道DropBox可以当Web服务器用吗?你知道怎么做出跨平台的幻灯片效果吗?不借助插件,怎样在移动设备上实现动画效果?怎样快速搭建和测试HTML电子邮箱?怎样制作跨PC和移动设备显示的应用界面?怎样利用最新的JavaScript框架(Backbone和Knockout)提高应用的响应速度?怎样有效利用CoffeeScript和S......一起来看看 《Web开发秘方》 这本书的介绍吧!