06使用 Go 讀寫請求

簡介

使用 web 框架, 最基礎(chǔ)的事情就是讀寫請求了,
Gin 支持很多種類的請求參數(shù), 也支持多種格式的響應.

讀取請求參數(shù)

path 中的參數(shù)

使用 Param() 可以獲取 path 中的參數(shù).

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

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

*action 是可選的, 如果不存在, 就會忽略掉, 比如是可以匹配到 /user/john/ 的,
另外 /user/john 會被跳轉(zhuǎn)到 /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 中的參數(shù)

使用 Query()DefaultQuery() 可以獲取 query 中的參數(shù),
后者使用第二個參數(shù)作為默認值.

// 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 中的參數(shù)

對于 form (表單) 中的參數(shù), 也有和 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,
  })
})

模型綁定

上面的幾種獲取參數(shù)的方式都比較常規(guī), 我覺得最有用的就是 模型綁定 了.

模型綁定首先要定義一個結(jié)構(gòu)體 struct, struct 需要設(shè)置相應的 tag, 就是那些在反引號 ` 里面的字段,
然后就可以用對應的數(shù)據(jù)填充這個 struct 了, 也就是綁定.

// 綁定 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
  // 根據(jù) 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 格式的請求體和普通的表單.

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

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

返回響應

對于請求, 也有多種類型的數(shù)據(jù)響應格式, 支持 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 服務(wù)來說, 這些已經(jīng)足夠用了, 主要還是用 JSON 格式的輸出.
如果需要高性能, 可以使用 ProtoBuf, 但這不是人類易讀的, 所以通常
來說 JSON 足以滿足要求.

總結(jié)

主要介紹了如何使用 Gin 讀取請求, 并返回響應.
這部分是 web 框架的基礎(chǔ), 框架的好用與否很大程度上
取決于這部分.

當前部分的代碼

作為版本 v0.6.0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容