丽水信息:Gin框架系列04:趣谈参数绑定与校验

admin 6个月前 (04-11) 科技 47 0

导读

在第二节,我们学习了Gin框架的路由界说与参数吸收,今天应一位同砚的要求,来解说一下参数的绑定与校验。

为什么校验参数?

本不必抛出这个问题的,但顾及到初出茅庐的同砚,这里注释一下。

假设做一个注册接口,传过来的用户名是不是不能骚气?好比一堆格和符号之类的;密码是不是不能太长也不能太短?手机号是不是要相符规则?性别是不是不能填人妖?

另外,登录的时刻我们也需要验证账号密码是不是准确的,那么为了利便上手,咱就先来个简朴示例,做登录验证。

激情演示

做登录之前得先想清晰需要对用户名密码做什么样的限制,好比他们都不能为空、用户名只能是字母或数字、密码长度只能在6位到12位之间等,若是列位看官没有异议,接下来我就拿上述的这几个条件来演示了。

界说结构体与接口

首先得有个地方存吸收到的用户名、密码参数,那就定一个名叫Login的结构体吧。

type Login struct {
	User     string
	Password string
}

然后再界说一个登录接口,这块不知道啥意思的同砚可以去看我的第二篇教程。

router.POST("/login", func(c *gin.Context) {

})

接口是定了,怎么才能把吸收的参数放到Login结构体里去呢?

丽水信息:Gin框架系列04:趣谈参数绑定与校验 第1张

绑定参数

我们去翻一下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框架系列04:趣谈参数绑定与校验 第2张

不行,我要一层一层剥开gin框架是怎么处置的。

丽水信息:Gin框架系列04:趣谈参数绑定与校验 第3张

绑定失败,剖析源码

经由长达60分钟的经心研究,我终于发现了最终奥义,先把我绘制的图贴上。

丽水信息:Gin框架系列04:趣谈参数绑定与校验 第4张

事情是这样的,我们挪用的Bind方式现实挪用了两个方式binding.Defaultc.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方式主要就干了两件事情,第一是剖析传过来的表单参数,第二是找到结构体里的tagform举行匹配赋值。看到这里我就明了了,原来只需要在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框架系列04:趣谈参数绑定与校验 第5张

鉴于官方文档给的信息太少了,我们照样通过源码去找更多线索吧,通过源代码可以看到,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的文件,有异常多的示例与注释,简直找到宝藏了,去他的官方文档。

丽水信息:Gin框架系列04:趣谈参数绑定与校验 第6张

我在内里找到了长度限制的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:趣谈参数绑定与校验 第7张

,

Sunbet

www.ipvps.cn信誉来自于每一位客户的口碑,Sunbet携手江苏安腾科技有限公将致力服务好每位Sunbet会员。!

AllBetGaming声明:该文看法仅代表作者自己,与本平台无关。转载请注明:丽水信息:Gin框架系列04:趣谈参数绑定与校验

网友评论

  • (*)

最新评论

站点信息

  • 文章总数:657
  • 页面总数:0
  • 分类总数:8
  • 标签总数:964
  • 评论总数:246
  • 浏览总数:10116