使用 Go 讀寫請求

簡介

使用 web 框架, 最基礎的事情就是讀寫請求了, Gin 支持不少種類的請求參數, 也支持多種格式的響應.git

讀取請求參數

path 中的參數

使用 Param() 能夠獲取 path 中的參數.github

定義在 path 中的參數有兩種格式, 一個是 :name 的以冒號開頭的, 另外一種是 *action 的以星號開頭的.web

:name 是一定匹配的, 必定要有值, 不能爲空. 下面的代碼中, 第一個例子就是如此, 用 :name 來表示用戶的名字, 這樣就能夠在路徑中表示任意的用戶名了.json

*action 是可選的, 若是不存在, 就會忽略掉, 好比是能夠匹配到 /user/john/ 的, 另外 /user/john 會被跳轉到 /user/john/.框架

// This handler will match /user/john but will not match /user/ or /user
router.GET("/user/:name", func(c *gin.Context) {
  name := c.Param("name")
  c.String(http.StatusOK, "Hello %s", name)
})

// However, this one will match /user/john/ and also /user/john/send
// If no other routers match /user/john, it will redirect to /user/john/
router.GET("/user/:name/*action", func(c *gin.Context) {
  name := c.Param("name")
  action := c.Param("action")
  message := name + " is " + action
  c.String(http.StatusOK, message)
})
複製代碼

query 中的參數

使用 Query()DefaultQuery() 能夠獲取 query 中的參數, 後者使用第二個參數做爲默認值.post

// Query string parameters are parsed using the existing underlying request object.
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
  firstname := c.DefaultQuery("firstname", "Guest")
  lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")

  c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
複製代碼

from 中的參數

對於 form (表單) 中的參數, 也有和 query 相似的方法, PostFormDefaultPostForm.性能

router.POST("/form_post", func(c *gin.Context) {
  message := c.PostForm("message")
  nick := c.DefaultPostForm("nick", "anonymous")

  c.JSON(200, gin.H{
    "status":  "posted",
    "message": message,
    "nick":    nick,
  })
})
複製代碼

模型綁定

上面的幾種獲取參數的方式都比較常規, 我以爲最有用的就是 模型綁定 了.ui

模型綁定首先要定義一個結構體 struct, struct 須要設置相應的 tag, 就是那些在反引號 ` 裏面的字段, 而後就能夠用對應的數據填充這個 struct 了, 也就是綁定.this

// 綁定 JSON
type Login struct {
	User     string `form:"user" json:"user" xml:"user" binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

// 綁定 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"})
})

// 綁定 XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <user>user</user>
// <password>123</password>
// </root>)
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"})
})

// 綁定 HTML 表單 (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
  var form Login
  // 根據 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"})
})
複製代碼

上面的代碼顯示了三種不一樣的綁定, 分別是綁定 JSON 格式的請求體, XML 格式的請求體和普通的表單.url

對於 query 也是使用 form tag 進行標記.

另外也能夠綁定 Header (使用 header tag) 和 Uri (使用 uri tag) 等.

返回響應

對於請求, 也有多種類型的數據響應格式, 支持 XML, JSON, YAML 和 ProtoBuf.

func main() {
	r := gin.Default()

	// gin.H is a shortcut for map[string]interface{}
	r.GET("/someJSON", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})

	r.GET("/moreJSON", func(c *gin.Context) {
		// You also can use a struct
		var msg struct {
			Name    string `json:"user"`
			Message string
			Number  int
		}
		msg.Name = "Lena"
		msg.Message = "hey"
		msg.Number = 123
		// Note that msg.Name becomes "user" in the JSON
		// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
		c.JSON(http.StatusOK, msg)
	})

	r.GET("/someXML", func(c *gin.Context) {
		c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})

	r.GET("/someYAML", func(c *gin.Context) {
		c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})

	r.GET("/someProtoBuf", func(c *gin.Context) {
		reps := []int64{int64(1), int64(2)}
		label := "test"
		// The specific definition of protobuf is written in the testdata/protoexample file.
		data := &protoexample.Test{
			Label: &label,
			Reps:  reps,
		}
		// Note that data becomes binary data in the response
		// Will output protoexample.Test protobuf serialized data
		c.ProtoBuf(http.StatusOK, data)
	})

	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}
複製代碼

對於 API 服務來講, 這些已經足夠用了, 主要仍是用 JSON 格式的輸出. 若是須要高性能, 可使用 ProtoBuf, 但這不是人類易讀的, 因此一般 來講 JSON 足以知足要求.

總結

主要介紹瞭如何使用 Gin 讀取請求, 並返回響應. 這部分是 web 框架的基礎, 框架的好用與否很大程度上 取決於這部分.

當前部分的代碼

做爲版本 v0.6.0

相關文章
相關標籤/搜索