Gin golang web development model binding implementation process analysis

  • 2020-11-18 06:17:20
  • OfStack

We learned that gin can get the parameters submitted to the front end through methods like DefaultQuery or DefaultPostForm. This works well with a small number of arguments, but if you think about it, if the interface has a large number of arguments, you'll have to call a parameter collection method many times. This article introduces a new way to receive parameters to solve this problem: model binding.

The model binding in gin can be understood as mapping the requested parameter to a specific type. gin supports multiple parameter formats, such as JSON, XML, YAML, and form parameters, and requires only the label to be declared on the corresponding field.

Bind a form or query string


type Person struct {
	Name  string `form:"name"`
	Address string `form:"address"`
}

func startPage(c *gin.Context) {
	var person Person
	if c.ShouldBindQuery(&person) == nil {
		log.Println(person.Name)
		log.Println(person.Address)
	}
	c.String(200, "Success")
}

Declare the form tag in the struct Name field and call the ShouldBindQuery method, and gin binds the name and address parameters in the query string for us. Note that although we declare the form tag, ShouldBindQuery binds only parameters in the query string.

If you want to bind the parameters in the form, you don't need to change the structure. You need to change the ShouldBindQuery side to the ShouldBind method. The ShouldBind method distinguishes between GET and POST requests, if they are parameters in the GET request binding query string, and if they are in the POST request binding form parameters, but cannot bind both parameters.

Bind the json parameter


type Person struct {
	Name  string `json:"name"`
	Address string `json:"address"`
}

func startPage(c *gin.Context) {
	var person Person
	if c.ShouldBind(&person) == nil {
		log.Println(person.Name)
		log.Println(person.Address)
	}
	c.String(200, "Success")
}

json is a commonly used data exchange format and seems to have become a DE facto standard, especially when interacting with web front-end pages. gin binds the json format data method as simple as setting the field label to json and calling the ShouldBind method.

Other types of parameter bindings

The routing parameter is labeled uri at binding time and the ShouldBindUri method is called.


type Person struct {
	Id  string `uri:"id"`
}

func startPage(c *gin.Context) {
	var person Person
	if c.ShouldBindUri(&person) == nil {
		log.Println(person.Id)
	}
	c.String(200, "Success")
}

A parameter bound to HTTP Header, the field is labeled header, and the calling method is ShouldBindHeader.

Another less common array parameter is the field label set to form:"colors[]". Examples of the structure are shown below:


type myForm struct {
  Colors []string `form:"colors[]"`
}

File upload is a scenario where I rarely get parameters in the form of model binding, which is also supported in gin.


type ProfileForm struct {
	Name  string        `form:"name"`
	Avatar *multipart.FileHeader `form:"avatar"`
	// Avatars []*multipart.FileHeader `form:"avatar"`  Multi-file upload 
}

func main() {
	router := gin.Default()
	router.POST("/profile", func(c *gin.Context) {
		var form ProfileForm
		if err := c.ShouldBind(&form); err != nil {
			c.String(http.StatusBadRequest, "bad request")
			return
		}

		err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename)
		if err != nil {
			c.String(http.StatusInternalServerError, "unknown error")
			return
		}

		c.String(http.StatusOK, "ok")
	})
	router.Run(":8080")
}

Multiple types of model bindings

If we have an UpdateUser interface, PUT /user/:id, the parameter is {"nickname": "nickname..." , "mobile" : "13322323232"}. The code is as follows:


type ProfileForm struct {
	Id    int  `uri:"id"`
	Nickname string `json:"nickname"` //  nickname 
	Mobile  string `json:"mobile"`  //  Mobile phone no. 
}

func main() {
	router := gin.Default()
	router.GET("/user/:id", func(c *gin.Context) {
		var form ProfileForm
		if err := c.ShouldBindUri(&form); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if err := c.ShouldBindJSON(&form); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		c.String(http.StatusOK, "ok")
	})
	router.Run(":8080")
}

It takes two calls to the bind method in the code to get all the arguments. After communicating with the gin community, we found that it is not possible to call one method and bind multiple parameter sources at the same time. The current version of gin is 1.6.x. We do not know whether this capability will be provided in the future.


Related articles: