使用 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

相关文章
相关标签/搜索