

Gin框架系列04:趣谈参数绑定与校验
source link: http://www.cnblogs.com/pingyeaa/p/12674589.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

导读
在第二节,我们学习了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语言与技术原理。

Recommend
-
69
-
55
背景 随着APP版本的迭代,Hybrid交互协议与页面跳转协议越来越多,带来了诸多问题。 如下: 协议文档平台更新不...
-
11
前言 最近优化 gin+vue的前后端分离项目 代码时候,发现代码中对请求数据的校验比较繁琐,于是想办法简化它。最终我发现了
-
23
最近自己也在用gin框架做项目,然后在群里也有人问关于,参数验证的问题,今天有时间正好研究一下。 gin版本 : v1.6.2 基本验证 定义参数绑定结构体 account_io.go pack...
-
14
Gin 框架中,处理 JSON 格式的参数绑定时,默认采用的标准包 encoding/json,然而标准包不能满足我们的一些要求,比如兼容字符串整型、PHP空数组、时间格式等。 最简单的方式 开发 API 时,需要用到 ShouldBindJSON 绑定传入的参...
-
14
结合Flask 与 marshmallow快速进行参数校验 在Flask里如何方便快速的进行参数校验呢?如下,我们通过组合Flask提供的工具函数,以及marshmallow,来完成一个方便快捷的参数 校验函数。 from flask import abort, make_response, json...
-
16
公众号中分享了一篇文章,关于SpringBoot集成validation校验参数的,粉丝留言说有坑。 原留言如下: 有坑,你试试^A-\\d{12}-\\d{4}$,这条正则经过validate这个方法无论参数写的对不对都会报验证错误,而用main方法测试是正常的。。 话...
-
6
Gin Request 绑定失败时自定义Message蜂鸟的小窝 Gin Request 绑定失败时自定义Message 我用GIN,做为我的WEB框架,并且一直使用它。一直以来有一个问题一直困绕着我,...
-
10
GIN 是如何绑定参数的 在GIN这个框架里,可以通过 Bind 系列的函数绑定并且校验参数,我们来看看是如何实现的。 GIN 的binding分为两个系列: ShouldBind 如果参数无法通过校验,就会返回错误给调用...
-
4
五分钟技术趣谈 | 基于Speech框架实现APP智能语音交互的解决方案 作者:移动Labs 2023-07-16 18:46:30 移动开发 本文透过和家亲上的实践...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK