获取 POST 参数
在 Gin 中,POST 请求的 Body 可以包含多种格式,不同格式对应不同的读取方式。常见的包括:
application/x-www-form-urlencoded(form 表单)
multipart/form-data(文件上传)
application/json(JSON)
- 原始字节流(raw body)
读取 form body
当请求使用 application/x-www-form-urlencoded 发送数据时,可以通过 c.PostForm 获取 form 表单中的字段。
注意:
c.PostForm 只适用于 form 表单类型的请求,不适用于 application/json 类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "io"
"github.com/gin-gonic/gin" )
func main() { r := gin.Default()
r.POST("/form-body", func(c *gin.Context) { name := c.PostForm("name") age := c.PostForm("age") c.JSON(200, gin.H{ "name": name, "age": age, }) })
r.Run() }
|
请求示例:

读取 form body 并设置默认值
和 GET 请求中的 Query 类似,POST form 也支持默认值。
1 2
| name := c.DefaultPostForm("name", "default-name") age := c.DefaultPostForm("age", "default-age")
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "io"
"github.com/gin-gonic/gin" )
func main() { r := gin.Default()
r.POST("/form-default-body", func(c *gin.Context) { name := c.DefaultPostForm("name", "default-name") age := c.DefaultPostForm("age", "default-age") c.JSON(200, gin.H{ "name": name, "age": age, }) })
r.Run() }
|
请求示例:

直接读取字节流(Raw Body)
在 Gin 中,c.Request.Body 实质上是一个 io.ReadCloser,底层是字节流。对于 application/json(或任何非 form 类型)请求,你可以直接手动读取原始 body:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package main
import ( "bytes" "io"
"github.com/gin-gonic/gin" )
func main() { r := gin.Default()
r.POST("/raw-body", func(c *gin.Context) { body, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(500, gin.H{ "message": "Internal Server Error", }) c.Abort() return } defer c.Request.Body.Close()
c.JSON(200, gin.H{ "body": string(body), }) })
r.Run() }
|
请求示例:

说明:
- 通过
io.ReadAll 读取后,body 会被“消耗”掉,后续再读取时将为空。 - 读取到的 body 是
[]byte,需要 string(body) 才能看到内容。
回写字节流(让 body 可再次读取)
如前所述,io.ReadAll 读取 body 后,body 的内容会被清空,因此你无法在读取 raw body 后再继续使用 PostForm 读取字段。
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| r.POST("/raw-body2", func(c *gin.Context) { body, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(500, gin.H{ "message": "Internal Server Error", }) c.Abort() return }
name := c.PostForm("name") age := c.PostForm("age")
defer c.Request.Body.Close() c.JSON(200, gin.H{ "name": name, "age": age, "body": string(body), }) })
|
请求示例:

解决方法:回写字节流
通过 io.NopCloser + bytes.NewBuffer(body) 可以将 body“塞回去”,让后续代码可以继续读取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| r.POST("/raw-body3", func(c *gin.Context) { body, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(500, gin.H{ "message": "Internal Server Error", }) c.Abort() return }
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
name := c.PostForm("name") age := c.PostForm("age")
defer c.Request.Body.Close() c.JSON(200, gin.H{ "name": name, "age": age, "body": string(body), }) })
|
请求示例:
