3.1 绑定相关名词
- 绑定(Binding)
- 把 HTTP 请求中的数据(JSON body、query、form、uri、header)反序列化并填充到 Go 结构体的过程。
- struct tag
- 结构体字段后的反引号元数据,如
`json:"name" binding:"required"`,告诉绑定器字段名与校验规则。 - ShouldBind 系列
- 绑定失败返回 error 由你处理(不自动响应),推荐使用:
ShouldBindJSON、ShouldBindQuery、ShouldBindUri等。 - MustBind / Bind 系列
- 绑定失败自动写 400 并终止,灵活性低,一般不用。
- validator.v10
- Gin 内置的校验引擎(go-playground/validator),通过
bindingtag 声明 required/email/min/max 等规则。 - Content-Type 推断
ShouldBind会根据请求头自动选 JSON/XML/Form 绑定器;显式方法(ShouldBindJSON)则强制格式。- multipart/form-data
- 用于文件上传的表单编码,Gin 用
c.FormFile/c.MultipartForm读取。
3.2 绑定 JSON Body
type CreateUserReq struct {
Name string `json:"name" binding:"required,min=2,max=32"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"gte=0,lte=150"`
}
func createUser(c *gin.Context) {
var req CreateUserReq
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// req 已经过校验,可以安全使用
c.JSON(201, gin.H{"created": req.Name})
}
$ curl -X POST localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"Al","email":"bad","age":20}'
# {"error":"Key: 'CreateUserReq.Email' Error:Field validation for 'Email' failed on the 'email' tag"}
3.3 绑定 Query 与 Uri
// 分页查询:?page=2&size=20&sort=created_at
type PageQuery struct {
Page int `form:"page,default=1" binding:"gte=1"`
Size int `form:"size,default=10" binding:"gte=1,lte=100"`
Sort string `form:"sort"`
}
func list(c *gin.Context) {
var q PageQuery
if err := c.ShouldBindQuery(&q); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, q)
}
// 绑定路径参数:/users/:id
type UserUri struct {
ID uint `uri:"id" binding:"required"`
}
func getUser(c *gin.Context) {
var u UserUri
if err := c.ShouldBindUri(&u); err != nil {
c.JSON(400, gin.H{"error": "invalid id"})
return
}
c.JSON(200, gin.H{"id": u.ID})
}
tag 对应关系
JSON body 用 json:;query/form 用 form:;路径参数用 uri:;请求头用 header:。同一个结构体可以混用多个 tag 供不同绑定方法复用。
3.4 常用 binding 校验规则
| 规则 | 含义 | 示例 |
|---|---|---|
| required | 非零值必填 | binding:"required" |
| 合法邮箱 | binding:"email" | |
| min/max | 字符串长度或数值范围 | binding:"min=6,max=20" |
| gte/lte | 大于等于 / 小于等于 | binding:"gte=0,lte=150" |
| oneof | 枚举值之一 | binding:"oneof=male female" |
| len | 固定长度 | binding:"len=11" |
| eqfield | 与另一字段相等 | binding:"eqfield=Password" |
| url/uuid | 合法 URL / UUID | binding:"url" |
3.5 表单与文件上传
// 普通表单字段 + 单文件
func upload(c *gin.Context) {
name := c.PostForm("name") // 表单字段
file, err := c.FormFile("file") // *multipart.FileHeader
if err != nil {
c.JSON(400, gin.H{"error": "file required"})
return
}
// 保存到磁盘
dst := "./uploads/" + file.Filename
if err := c.SaveUploadedFile(file, dst); err != nil {
c.JSON(500, gin.H{"error": "save failed"})
return
}
c.JSON(200, gin.H{"name": name, "size": file.Size})
}
// 多文件
func uploadMulti(c *gin.Context) {
form, _ := c.MultipartForm()
files := form.File["files"]
for _, f := range files {
c.SaveUploadedFile(f, "./uploads/"+f.Filename)
}
c.JSON(200, gin.H{"count": len(files)})
}
限制上传体积
默认内存缓冲 32 MiB。调大或调小用 r.MaxMultipartMemory = 8 << 20(8 MiB)。超大文件应流式落盘,切勿全部读进内存。生产还要校验扩展名/MIME、随机化文件名防路径穿越。
3.6 自定义验证器
validator 支持注册自定义规则。例如校验手机号:
import (
"regexp"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
var phoneRe = regexp.MustCompile(`^1[3-9]\d{9}$`)
var mobile validator.Func = func(fl validator.FieldLevel) bool {
return phoneRe.MatchString(fl.Field().String())
}
func initValidator() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("mobile", mobile)
}
}
// 使用:
type Req struct {
Phone string `json:"phone" binding:"required,mobile"`
}
3.7 绑定策略小结
- 始终用
ShouldBindXxx,自己处理错误响应,别用会自动 400 的BindXxx。 - 请求参数一律定义 DTO 结构体 + binding tag,声明式校验最清爽。
- 文件上传注意体积上限、MIME 校验与安全的落盘路径。
- 复杂业务规则用自定义 validator,全局注册一次即可。
- 下一章讲 响应——JSON/XML/流式与统一响应结构封装。