基於gin的golang web開發:模型繫結

陳巨集博發表於2020-10-28

在前兩篇文章介紹路由的時候,我們瞭解到gin可用通過類似DefaultQuery或DefaultPostForm等方法獲取到前端提交過來的引數。引數不多的情況下也很好用,但是想想看,如果介面有很多個引數的時候再用這種方法就要呼叫很多次獲取引數的方法,本文將介紹一種新的接收引數的方法來解決這個問題:模型繫結。

gin中的模型繫結可以理解為:把請求的引數對映為一個具體的型別。gin支援JSON,XML,YAML和表單引數等多種引數格式,只需要在對應的欄位上宣告標籤。

繫結表單或者查詢字串

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")
}

在結構體Name欄位宣告form標籤,並呼叫ShouldBindQuery方法,gin會為我們繫結查詢字串中的name和address兩個引數。注意雖然我們宣告瞭form標籤,ShouldBindQuery只繫結查詢字串中的引數。

如果你想繫結表單中的引數的話結構體不用改變,需要把ShouldBindQuery方更改為ShouldBind方法。ShouldBind方法會區分GET和POST請求,如果是GET請求繫結查詢字串中的引數,如果是POST請求繫結表單引數中的內容,但是不能同時繫結兩種引數。

繫結json引數

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是一種常用的資料交換格式,尤其是在和web前端頁面互動的時候,似乎已經成為了一種事實標準。gin繫結json格式資料方法很簡單,只需要設定欄位的標籤為json並且呼叫ShouldBind方法。

其他型別引數繫結

路由引數在繫結時設定標籤為uri,並呼叫ShouldBindUri方法。

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")
}

繫結在HTTP Header中的引數,欄位的標籤設定為header,呼叫方法為ShouldBindHeader。

還有不太常用的陣列引數是欄位標籤設定為form:"colors[]",結構體例子如下:

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

檔案上傳這種場景我很少用模型繫結的方式獲取引數,在gin中對於這種場景也提供了模型繫結支援。

type ProfileForm struct {
	Name   string                `form:"name"`
	Avatar *multipart.FileHeader `form:"avatar"`
	// Avatars []*multipart.FileHeader `form:"avatar"` 多檔案上傳
}

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")
}

多種型別的模型繫結

如果我們有一個UpdateUser介面,PUT /user/:id,引數是{"nickname": "nickname...","mobile": "13322323232"}。程式碼如下:

type ProfileForm struct {
	Id       int    `uri:"id"`
	Nickname string `json:"nickname"` // 暱稱
	Mobile   string `json:"mobile"`   // 手機號
}

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")
}

程式碼裡呼叫了兩次bind方法才獲取到全部的引數。和gin社群溝通之後發現目前還不能呼叫一個方法同時繫結多個引數來源,當前gin版本為1.6.x,不知道未來會不會提供這種功能。

文章出處:基於gin的golang web開發:模型繫結

相關文章