简介
使用 web 框架, 最基础的事情就是读写请求了, Gin 支持很多种类的请求参数, 也支持多种格式的响应.
读取请求参数
path 中的参数
使用 Param()
可以获取 path 中的参数.
定义在 path 中的参数有两种格式, 一个是 :name
的以冒号开头的,
另一种是 *action
的以星号开头的.
:name
是必定匹配的, 一定要有值, 不能为空. 下面的代码中, 第一个例子就是如此,
用 :name
来表示用户的名字, 这样就可以在路径中表示任意的用户名了.
*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 中的参数,
后者使用第二个参数作为默认值.
// 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 类似的方法, PostForm
和 DefaultPostForm
.
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,
})
})
复制代码
模型绑定
上面的几种获取参数的方式都比较常规, 我觉得最有用的就是 模型绑定 了.
模型绑定首先要定义一个结构体 struct, struct 需要设置相应的 tag, 就是那些在反引号 ` 里面的字段, 然后就可以用对应的数据填充这个 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
// 根据 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) 等.
返回响应
对于请求, 也有多种类型的数据响应格式, 支持 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
有疑问加站长微信联系(非本文作者)