实际开发中,一个请求可能包含多种参数:路径参数、查询参数、请求体。Gin 支持同时绑定多个来源的参数。
最简单的方式是分别绑定:
type UserParams struct {
ID string `uri:"id" binding:"required"`
}
type UserQuery struct {
Fields string `form:"fields"`
}
type UserBody struct {
Name string `json:"name"`
Email string `json:"email"`
}
r.PUT("/users/:id", func(c *gin.Context) {
var params UserParams
if err := c.ShouldBindUri(¶ms); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
var query UserQuery
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
var body UserBody
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"id": params.ID,
"fields": query.Fields,
"name": body.Name,
"email": body.Email,
})
})
可以把多个来源的参数合并到一个结构体:
type UpdateUserRequest struct {
ID string `uri:"id" binding:"required"`
Fields string `form:"fields"`
Name string `json:"name"`
Email string `json:"email"`
}
r.PUT("/users/:id", func(c *gin.Context) {
var req UpdateUserRequest
if err := c.ShouldBindUri(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, req)
})
type Request struct {
Token string `header:"Authorization"`
Language string `header:"Accept-Language"`
}
r.GET("/info", func(c *gin.Context) {
var req Request
if err := c.ShouldBindHeader(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"token": req.Token,
"language": req.Language,
})
})
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
type FullRequest struct {
ID string `uri:"id" binding:"required"`
Version string `form:"version"`
Token string `header:"Authorization" binding:"required"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
r := gin.Default()
r.PUT("/users/:id", updateUser)
r.Run(":8080")
}
func updateUser(c *gin.Context) {
var req FullRequest
if err := c.ShouldBindUri(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的查询参数"})
return
}
if err := c.ShouldBindHeader(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "缺少认证信息"})
return
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求体"})
return
}
c.JSON(http.StatusOK, gin.H{
"id": req.ID,
"version": req.Version,
"token": req.Token,
"name": req.Name,
"email": req.Email,
})
}
可以封装一个辅助函数简化绑定:
func bindAll(c *gin.Context, req interface{}) error {
if err := c.ShouldBindUri(req); err != nil {
return err
}
if err := c.ShouldBindQuery(req); err != nil {
return err
}
if err := c.ShouldBindHeader(req); err != nil {
return err
}
if err := c.ShouldBindJSON(req); err != nil {
return err
}
return nil
}
r.PUT("/users/:id", func(c *gin.Context) {
var req FullRequest
if err := bindAll(c, &req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, req)
})
这一章学习了: