手动表单验证_Golang GinWeb框架4-请求参数绑定和验证

简介


本文接着上文(Golang GinWeb框架3-自定义日志格式和输出方式/启禁日志颜色)继续探索GinWeb框架

9e1c97120aa59b5898cceb30c8e80925.png

模型绑定和验证


使用模型绑定来绑定请求体到一个Go类型上. 目前支持JSON,XML,YAML以及标准表单(如foo=bar&boo=baz)的绑定.

Gin使用go-playground/validator/v10包来验证请求, 关于tags在验证中使用详见(https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags)

注意:绑定前请确认结构体中需要绑定的字段标签与绑定类型一致,比如绑定JSON,设置标签: json:"fieldname"

Gin提供两种方式(类型)来完成绑定:

  • Must bind

1. 方法: Bind, BindJSON, BindXML, BindQuery, BindYAML, BindHeader

2. 特点: 这些方法底层使用MustBindWith方法. 如果出现绑定错误, 请求将以状态码400返回失败信息:c.AbortWithError(400, err).SetType(ErrorTypeBind), 响应中设置Content-Type头为text/plain; charset=utf-8.如果手动设置响应码,会警告响应头已经设置,比如提示: [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422, 如果想要更好的控制这些行为,建议使用下面对应的ShoudBind方法.

  • Should bind

1. 方法: ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML, ShouldBindHeader

2. 特点: 这些方法底层使用ShouldBindWith. 如果出现绑定错误, 会返回错误, 开发者可以控制和恰当的处理这些错误.

当使用绑定方法时, Gin尝试根据类型头Content-Type header自动推断要使用的绑定器. 如果你已经确认需要绑定的类型,可以直接使用底层的MustBindWith或ShouldBindWith方法.

你也可以针对特殊的字段指定required标签值, 如果某个字段指定了:binding:"required"标签, 但是在绑定时该字段为空会返回错误.

如以下代码绑定JSON:

package main​import (  "github.com/gin-gonic/gin"  "net/http")​// Binding from JSONtype Login struct {  User string `form:"user" json:"user" xml:"user"  binding:"required"` //分别定义form,json,xml,binding等标签  //Password string `form:"password" json:"password" xml:"password" binding:"required"`  Password string `form:"password" json:"password" xml:"password" binding:"-"`}​​func main() {  router := gin.Default()​  // Example for binding JSON ({"user": "manu", "password": "123"})  router.POST("/loginJSON", func(c *gin.Context) {    var json Login    if err := c.ShouldBindJSON(&json); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return    }​    if json.User != "manu" || json.Password != "123" {      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})      return    }​    c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  })​  // Example for binding XML (  //  <?xml version="1.0" encoding="UTF-8"?>  //    //    user  //    123  //  )  router.POST("/loginXML", func(c *gin.Context) {    var xml Login    if err := c.ShouldBindXML(&xml); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return    }​    if xml.User != "manu" || xml.Password != "123" {      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})      return    }​    c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  })​  // Example for binding a HTML form (user=manu&password=123)  router.POST("/loginForm", func(c *gin.Context) {    var form Login    // This will infer what binder to use depending on the content-type header.    if err := c.ShouldBind(&form); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return    }​    if form.User != "manu" || form.Password != "123" {      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})      return    }​    c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  })​  // Listen and serve on 0.0.0.0:8080  router.Run(":8080")}​//模拟请求: curl -v -X POST http://localhost:8080/loginJSON -H 'content-type: application/json' -d '{ "user": "manu", "password": "123" }'

跳过验证: 与binding:"required"标签对应的是binding:"-", 表示该字段不做绑定, 所以绑定时该字段为空不会报错.

自定义验证器


你也可以自己注册一个自定义验证器, 示例代码请参考(https://github.com/gin-gonic/examples/blob/master/custom-validation/server.go)

package main​import (  "net/http"  "time"​  "github.com/gin-gonic/gin"  "github.com/gin-gonic/gin/binding"  "github.com/go-playground/validator/v10")​// Booking contains binded and validated data.// Booking结构中定义了包含绑定器和日期验证器标签type Booking struct {  CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`  //登记时间  CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`  //gtfield=CheckIn表示结账时间必须大于登记时间}​// 定义日期验证器var bookableDate validator.Func = func(fl validator.FieldLevel) bool {  date, ok := fl.Field().Interface().(time.Time)  //利用反射获取到字段值 -> 转为接口 -> 类型断言(时间类型)  if ok {    today := time.Now()    if today.After(date) {  //如果当前时间在checkIn字段时间之后,返回false,即登记时间不能早于当前的时间      return false    }  }  return true}​func main() {  route := gin.Default()  //对binding.Validator.Engine()接口进行类型断言,断言类型为Validate结构,如果断言成功,就将自定义的验证器注册到Gin内部  if v, ok := binding.Validator.Engine().(*validator.Validate); ok {    // - if the key already exists, the previous validation function will be replaced. 该注册方法会将已经存在的验证器替换    // - this method is not thread-safe it is intended that these all be registered prior to any validation    // 注册方法不是线程安全的, 在验证开始前,需要保证所有的验证器都注册成功    v.RegisterValidation("bookabledate", bookableDate)  }​  route.GET("/bookable", getBookable)  route.Run(":8085")}​func getBookable(c *gin.Context) {  var b Booking  if err := c.ShouldBindWith(&b, binding.Query); err == nil {    c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})  } else {    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  }}​//模拟请求:// 登记时间和结账时间符合条件//$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17"//{"message":"Booking dates are valid!"} 登记时间在结账时间之后, 不满足gtfield校验规则//$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09"//{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"} 登记时间在当前时间之前,不满足自定义的验证器//$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10"//{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}%

另外结构体级别的验证采用如下的方式注册, v.RegisterStructValidation(UserStructLevelValidation, User{}), 请参考struct-lvl-validation example(https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations)

参考文档


Gin官方仓库:https://github.com/gin-gonic/gin


END已结束

2bd5efc932f4d4096d7ae30479ca6e33.png

欢迎大家留言, 订阅, 交流哦!


85d983d625a2de6d5889e2d2c723a4df.gif

往期回顾


Golang GinWeb框架3-自定义日志格式和输出方式/启禁日志颜色

Golang GinWeb框架2-文件上传/程序panic崩溃后自定义处理方式

Golang GinWeb框架-快速入门/参数解析

Golang与亚马逊对象存储服务AmazonS3快速入门

Golang+Vue实现Websocket全双工通信入门

GolangWeb编程之控制器方法HandlerFunc与中间件Middleware

Golang连接MySQL执行查询并解析-告别结构体

Golang的一种发布订阅模式实现

Golang 并发数据冲突检测器(Data Race Detector)与并发安全

Golang"驱动"MongoDB-快速入门("快码加鞭")

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值