快速入门

  1. package main
  2. import "github.com/gin-gonic/gin"
  3. func main() {
  4. r := gin.Default()
  5. r.GET("/ping", func(c *gin.Context) {
  6. c.JSON(200, gin.H{
  7. "message": "pong",
  8. })
  9. })
  10. r.Run() // listen and serve on 0.0.0.0:8080
  11. }

restful使用方法,路径参数绑定

  1. func main() {
  2. router := gin.Default()
  3. // 此规则能够匹配/user/john这种格式,但不能匹配/user/ 或 /user这种格式
  4. router.GET("/user/:name", func(c *gin.Context) {
  5. name := c.Param("name")
  6. c.String(http.StatusOK, "Hello %s", name)
  7. })
  8. router.Run(":8080")
  9. }

获取Get参数

  1. func main() {
  2. router := gin.Default()
  3. // 匹配的url格式: /welcome?firstname=Jane&lastname=Doe
  4. router.GET("/welcome", func(c *gin.Context) {
  5. firstname := c.DefaultQuery("firstname", "Guest")
  6. lastname := c.Query("lastname")
  7. c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
  8. })
  9. router.Run(":8080")
  10. }

获取Post参数

  1. func main() {
  2. router := gin.Default()
  3. router.POST("/form_post", func(c *gin.Context) {
  4. message := c.PostForm("message")
  5. nick := c.DefaultPostForm("nick", "anonymous") // 此方法可以设置默认值
  6. c.JSON(200, gin.H{
  7. "status": "posted",
  8. "message": message,
  9. "nick": nick,
  10. })
  11. })
  12. router.Run(":8080")
  13. }

Get + Post 混合

  1. //POST /post?id=1234&page=1 HTTP/1.1
  2. // Content-Type: application/x-www-form-urlencoded
  3. //name=manu&message=this_is_great
  4. func main() {
  5. router := gin.Default()
  6. router.POST("/post", func(c *gin.Context) {
  7. id := c.Query("id")
  8. page := c.DefaultQuery("page", "0")
  9. name := c.PostForm("name")
  10. message := c.PostForm("message")
  11. fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
  12. })
  13. router.Run(":8080")
  14. }
  15. // 结果:id: 1234; page: 1; name: manu; message: this_is_great

真正的绑定技巧来了

其实gin提供了两套绑定方法,先来看看两套方法的概述信息:

  1. Must bind

    Methods: Bind, BIndJSON,BindXML,BindYAML

  2. Should bind:

    Methods: ShouldBind,ShouldBindJSON,ShouldBindXML, ShouldBindXML,ShouldBindYAML

    简单一点说,bindxxxx和shouldbindxxxx的区别:当产生异常的时候,会在header里面添加400的返回信息,而shouldbindxxxx不会。

当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith。

你还可以给字段指定特定规则的修饰符,如果一个字段用binding:”required”修饰,并且在绑定时该字段的值为空,那么将返回一个错误。如果我们使用binding:”-“,就不会校验这个字段。

但是这个require的key word对于struct却没有作用,这就需要我们使用dive标记
示例:

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/gin-gonic/gin"
  6. )
  7. type user struct {
  8. Name string `json:"name" binding:"required"`
  9. Email string `json:"email" binding:"required,email"`
  10. }
  11. type data struct {
  12. User []user `json:"user" binding:"required,dive"` // use dive tag
  13. }
  14. //GetDataTest will get test data
  15. func GetDataTest(c *gin.Context) {
  16. var data data
  17. err := c.Bind(&data)
  18. if err == nil {
  19. fmt.Printf("%+v", data)
  20. c.JSON(http.StatusOK, gin.H{
  21. "message": "done",
  22. })
  23. } else {
  24. c.JSON(http.StatusBadRequest, gin.H{
  25. "message": err.Error(),
  26. })
  27. }
  28. }
  29. func main(){
  30. route := gin.Default()
  31. route.POST("/", GetDataTest)
  32. route.Run(":8080")
  33. }
  34. //服务报错
  35. // $ curl -H "Content-Type: application/json" -X POST --data '{"user": [{"email": "alu@alu.com","name": "alu"},{"email": "","name": "alu"}]}' http://localhost:8080/
  36. // {"message":"Key: 'data.User[1].Email' Error:Field validation for 'Email' failed on the 'required' tag"}

有时候我们还可以自定义验证器

  1. package main
  2. import (
  3. "net/http"
  4. "reflect"
  5. "time"
  6. "github.com/gin-gonic/gin"
  7. "github.com/gin-gonic/gin/binding"
  8. "gopkg.in/go-playground/validator.v8"
  9. )
  10. // Booking contains binded and validated data.
  11. type Booking struct {
  12. CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
  13. CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
  14. }
  15. func bookableDate(
  16. v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
  17. field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
  18. ) bool {
  19. if date, ok := field.Interface().(time.Time); ok {
  20. today := time.Now()
  21. if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
  22. return false
  23. }
  24. }
  25. return true
  26. }
  27. func main() {
  28. route := gin.Default()
  29. if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
  30. v.RegisterValidation("bookabledate", bookableDate)
  31. }
  32. route.GET("/bookable", getBookable)
  33. route.Run(":8085")
  34. }
  35. func getBookable(c *gin.Context) {
  36. var b Booking
  37. if err := c.ShouldBindWith(&b, binding.Query); err == nil {
  38. c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
  39. } else {
  40. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  41. }
  42. }

omitempty的使用

omitempty的作用就是当该字段为空的时候则不输出,这个空可以是根本没有这个字段,也可能是空字符串。
在开发的过程中,遇到了一个小小的误区, 导致了程序出现了一些问题。我定义的数据结构是这样的:

  1. type LoginInfo struct {
  2. UserName string `json:"username" binding:"required"`
  3. PassWord string `json:"password,omitempty" binding:"required"`
  4. ClientID string `json:"clientid" binding:"required"`
  5. SockPort string `json:"sockport" binding:"required"`
  6. }

在需求中,password可以传空字符串。但是在这个VO的model中,password的tag为:json:"password,omitempty" binding:"required"。经过实践发现,password定义为omitempty同时binding为required,当password为空字符串(“”)的时候,会导致ShouldBindJSON异常。

参考:
go gin框架 binding:”required” 注解不起作用
Gin框架中文文档
gin的BindJSON和ShouldBindJSON,ShouldBindWith的区别