参数验证

Gin 使用 validator 库进行参数验证,通过结构体标签定义验证规则。

基本验证

binding 标签中添加验证规则:

type User struct {
    Name  string `binding:"required"`
    Email string `binding:"required,email"`
    Age   int    `binding:"required,gte=0,lte=150"`
}

常用验证规则

字符串验证

规则说明
required必填
min=3最小长度
max=20最大长度
len=10固定长度
email邮箱格式
urlURL 格式
uriURI 格式
alpha只含字母
alphanum字母和数字
numeric数字
hexadecimal十六进制
uuidUUID 格式
jsonJSON 格式
oneof=red green blue枚举值

数字验证

规则说明
eq=10等于
ne=0不等于
gt=0大于
gte=0大于等于
lt=100小于
lte=100小于等于

字段比较

规则说明
eqfield=Password等于另一字段
nefield=OldPassword不等于另一字段
gtfield=StartDate大于另一字段
gtefield=StartDate大于等于另一字段

示例

必填验证

type LoginRequest struct {
    Username string `binding:"required"`
    Password string `binding:"required"`
}

r.POST("/login", func(c *gin.Context) {
    var req LoginRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"message": "登录成功"})
})

长度验证

type RegisterRequest struct {
    Username string `binding:"required,min=3,max=20"`
    Password string `binding:"required,min=6,max=32"`
}

r.POST("/register", func(c *gin.Context) {
    var req RegisterRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"message": "注册成功"})
})

邮箱验证

type SubscribeRequest struct {
    Email string `binding:"required,email"`
}

r.POST("/subscribe", func(c *gin.Context) {
    var req SubscribeRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(400, gin.H{"error": "无效的邮箱地址"})
        return
    }

    c.JSON(200, gin.H{"message": "订阅成功"})
})

枚举验证

type FilterRequest struct {
    Status string `binding:"required,oneof=active inactive pending"`
    Sort   string `binding:"oneof=asc desc"`
}

r.GET("/items", func(c *gin.Context) {
    var req FilterRequest
    if err := c.ShouldBindQuery(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"status": req.Status, "sort": req.Sort})
})

密码确认

type ChangePasswordRequest struct {
    OldPassword string `binding:"required,min=6"`
    NewPassword string `binding:"required,min=6"`
    ConfirmPassword string `binding:"required,eqfield=NewPassword"`
}

r.POST("/password", func(c *gin.Context) {
    var req ChangePasswordRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(400, gin.H{"error": "两次密码输入不一致"})
        return
    }

    c.JSON(200, gin.H{"message": "密码修改成功"})
})

范围验证

type Pagination struct {
    Page     int `form:"page" binding:"required,gte=1"`
    PageSize int `form:"page_size" binding:"required,gte=1,lte=100"`
}

r.GET("/list", func(c *gin.Context) {
    var p Pagination
    if err := c.ShouldBindQuery(&p); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"page": p.Page, "page_size": p.PageSize})
})

跳过验证

使用 binding:"-" 跳过验证:

type User struct {
    Name  string `binding:"required"`
    Bio   string `binding:"-"`
}

可选字段

结合 omitempty 使用:

type UpdateUser struct {
    Name  string `binding:"omitempty,min=3"`
    Email string `binding:"omitempty,email"`
}

完整示例

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type CreateUserRequest struct {
    Username string `json:"username" binding:"required,min=3,max=20,alphanum"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,min=8,max=32"`
    Age      int    `json:"age" binding:"required,gte=1,lte=150"`
    Role     string `json:"role" binding:"required,oneof=admin user guest"`
}

type UpdateUserRequest struct {
    Username string `json:"username" binding:"omitempty,min=3,max=20,alphanum"`
    Email    string `json:"email" binding:"omitempty,email"`
    Age      int    `json:"age" binding:"omitempty,gte=1,lte=150"`
}

func main() {
    r := gin.Default()

    r.POST("/users", createUser)
    r.PATCH("/users/:id", updateUser)

    r.Run(":8080")
}

func createUser(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusCreated, gin.H{
        "message":  "用户创建成功",
        "username": req.Username,
    })
}

func updateUser(c *gin.Context) {
    id := c.Param("id")

    var req UpdateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "id":      id,
        "message": "用户更新成功",
    })
}

小结

这一章学习了:

  • 常用验证规则
  • 字符串、数字、字段比较验证
  • 枚举和范围验证
  • 可选字段处理