Golang GinWeb框架4-请求参数绑定和验证

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

 简介

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


模型绑定和验证

使用模型绑定来绑定请求体到一个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:

  1. package main 
  2.  
  3. import ( 
  4.   "github.com/gin-gonic/gin" 
  5.   "net/http" 
  6.  
  7. // Binding from JSON 
  8. type Login struct { 
  9.   User string `form:"user" json:"user" xml:"user"  binding:"required"` //分别定义form,json,xml,binding等标签 
  10.   //Password string `form:"password" json:"password" xml:"password" binding:"required"
  11.   Password string `form:"password" json:"password" xml:"password" binding:"-"
  12.  
  13. func main() { 
  14.   router := gin.Default() 
  15.  
  16.   // Example for binding JSON ({"user""manu""password""123"}) 
  17.   router.POST("/loginJSON", func(c *gin.Context) { 
  18.     var json Login 
  19.     if err := c.ShouldBindJSON(&json); err != nil { 
  20.       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 
  21.       return 
  22.     } 
  23.  
  24.     if json.User != "manu" || json.Password != "123" { 
  25.       c.JSON(http.StatusUnauthorized, gin.H{"status""unauthorized"}) 
  26.       return 
  27.     } 
  28.  
  29.     c.JSON(http.StatusOK, gin.H{"status""you are logged in"}) 
  30.   }) 
  31.  
  32.   // Example for binding XML ( 
  33.   //  <?xml version="1.0" encoding="UTF-8"?> 
  34.   //  <root> 
  35.   //    <user>user</user
  36.   //    <password>123</password
  37.   //  </root>) 
  38.   router.POST("/loginXML", func(c *gin.Context) { 
  39.     var xml Login 
  40.     if err := c.ShouldBindXML(&xml); err != nil { 
  41.       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 
  42.       return 
  43.     } 
  44.  
  45.     if xml.User != "manu" || xml.Password != "123" { 
  46.       c.JSON(http.StatusUnauthorized, gin.H{"status""unauthorized"}) 
  47.       return 
  48.     } 
  49.  
  50.     c.JSON(http.StatusOK, gin.H{"status""you are logged in"}) 
  51.   }) 
  52.  
  53.   // Example for binding a HTML form (user=manu&password=123) 
  54.   router.POST("/loginForm", func(c *gin.Context) { 
  55.     var form Login 
  56.     // This will infer what binder to use depending on the content-type header. 
  57.     if err := c.ShouldBind(&form); err != nil { 
  58.       c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 
  59.       return 
  60.     } 
  61.  
  62.     if form.User != "manu" || form.Password != "123" { 
  63.       c.JSON(http.StatusUnauthorized, gin.H{"status""unauthorized"}) 
  64.       return 
  65.     } 
  66.  
  67.     c.JSON(http.StatusOK, gin.H{"status""you are logged in"}) 
  68.   }) 
  69.  
  70.   // Listen and serve on 0.0.0.0:8080 
  71.   router.Run(":8080"
  72.  
  73. //模拟请求: 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)

  1. package main 
  2.  
  3. import ( 
  4.   "net/http" 
  5.   "time" 
  6.  
  7.   "github.com/gin-gonic/gin" 
  8.   "github.com/gin-gonic/gin/binding" 
  9.   "github.com/go-playground/validator/v10" 
  10.  
  11. // Booking contains binded and validated data. 
  12. // Booking结构中定义了包含绑定器和日期验证器标签 
  13. type Booking struct { 
  14.   CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`  //登记时间 
  15.   CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`  //gtfield=CheckIn表示结账时间必须大于登记时间 
  16.  
  17. // 定义日期验证器 
  18. var bookableDate validator.Func = func(fl validator.FieldLevel) bool { 
  19.   date, ok := fl.Field().Interface().(time.Time)  //利用反射获取到字段值 -> 转为接口 -> 类型断言(时间类型) 
  20.   if ok { 
  21.     today := time.Now() 
  22.     if today.After(date) {  //如果当前时间在checkIn字段时间之后,返回false,即登记时间不能早于当前的时间 
  23.       return false 
  24.     } 
  25.   } 
  26.   return true 
  27.  
  28. func main() { 
  29.   route := gin.Default() 
  30.   //对binding.Validator.Engine()接口进行类型断言,断言类型为Validate结构,如果断言成功,就将自定义的验证器注册到Gin内部 
  31.   if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 
  32.     // - if the key already exists, the previous validation function will be replaced. 该注册方法会将已经存在的验证器替换 
  33.     // - this method is not thread-safe it is intended that these all be registered prior to any validation 
  34.     // 注册方法不是线程安全的, 在验证开始前,需要保证所有的验证器都注册成功 
  35.     v.RegisterValidation("bookabledate", bookableDate) 
  36.   } 
  37.  
  38.   route.GET("/bookable", getBookable) 
  39.   route.Run(":8085"
  40.  
  41. func getBookable(c *gin.Context) { 
  42.   var b Booking 
  43.   if err := c.ShouldBindWith(&b, binding.Query); err == nil { 
  44.     c.JSON(http.StatusOK, gin.H{"message""Booking dates are valid!"}) 
  45.   } else { 
  46.     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 
  47.   } 
  48.  
  49. //模拟请求: 
  50. // 登记时间和结账时间符合条件 
  51. //$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" 
  52. //{"message":"Booking dates are valid!"
  53. // 
  54. // 登记时间在结账时间之后, 不满足gtfield校验规则 
  55. //$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" 
  56. //{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"
  57. // 
  58. // 登记时间在当前时间之前,不满足自定义的验证器 
  59. //$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" 
  60. //{"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

 

责任编辑:姜华 来源: 云原生云
相关推荐

2020-11-23 10:48:39

Golang GinW

2020-11-27 07:54:53

Golang GinW

2020-12-03 09:28:05

Golang GinW

2020-12-02 11:18:28

Golang GinW

2020-12-10 10:22:48

GinWeb中间件HTTPS

2022-10-17 09:02:49

Go自动验证数据绑定

2020-11-25 09:10:39

Golang GinW

2024-02-19 07:40:10

2020-11-25 09:18:15

Golang GinW

2020-12-08 12:05:48

Golang GinW框架HTTPS

2011-08-18 16:25:55

西蒙VersaPOD 4

2020-10-09 08:29:24

POSTGET参数

2018-12-17 09:32:41

HTTPAPIWeb

2023-10-22 20:20:37

FiberGo

2011-03-30 09:03:57

struts2

2013-04-17 09:47:10

Windows PhoWindows Pho

2023-10-26 11:07:48

Golang开发

2021-06-18 15:23:59

鸿蒙HarmonyOS应用

2014-04-09 10:18:21

ASP.NETMVC

2010-01-05 09:15:45

Java EE 6Bean验证
点赞
收藏

51CTO技术栈公众号