Gin 使用 validator 库进行参数验证,支持丰富的验证规则。掌握高级用法可以处理复杂的验证场景。
type User struct {
Name string `json:"name" binding:"required,min=2,max=50"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=8,max=20"`
Age int `json:"age" binding:"required,gte=0,lte=150"`
Phone string `json:"phone" binding:"omitempty,len=11"`
Website string `json:"website" binding:"omitempty,url"`
Role string `json:"role" binding:"required,oneof=admin user guest"`
}
常用规则:
required - 必填min,max - 字符串长度或数值范围gte,lte - 大于等于、小于等于gt,lt - 大于、小于email - 邮箱格式url - URL 格式oneof - 枚举值omitempty - 可选注册自定义验证规则:
package main
import (
"net/http"
"regexp"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
func validatePhone(fl validator.FieldLevel) bool {
phone := fl.Field().String()
matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
return matched
}
func main() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("phone", validatePhone)
}
r := gin.Default()
r.POST("/user", func(c *gin.Context) {
var user struct {
Name string `json:"name" binding:"required"`
Phone string `json:"phone" binding:"required,phone"`
}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "ok"})
})
r.Run(":8080")
}
验证多个字段的关联关系:
type RegisterForm struct {
Password string `json:"password" binding:"required,min=8"`
ConfirmPassword string `json:"confirm_password" binding:"required"`
}
func validateRegister(sl validator.StructLevel) {
form := sl.Current().Interface().(RegisterForm)
if form.Password != form.ConfirmPassword {
sl.ReportError(form.ConfirmPassword, "confirm_password", "ConfirmPassword", "eqfield", "")
}
}
func main() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterStructValidation(validateRegister, RegisterForm{})
}
r := gin.Default()
r.POST("/register", func(c *gin.Context) {
var form RegisterForm
if err := c.ShouldBindJSON(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "ok"})
})
r.Run(":8080")
}
var validationMessages = map[string]string{
"required": "此字段为必填项",
"email": "邮箱格式不正确",
"min": "长度不能少于%s",
"max": "长度不能超过%s",
"phone": "手机号格式不正确",
}
func getErrorMessage(err error) string {
if validationErrors, ok := err.(validator.ValidationErrors); ok {
for _, e := range validationErrors {
if msg, exists := validationMessages[e.Tag()]; exists {
if e.Param() != "" {
return fmt.Sprintf(msg, e.Param())
}
return msg
}
}
}
return "参数验证失败"
}
r.POST("/user", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": getErrorMessage(err),
})
return
}
c.JSON(http.StatusOK, user)
})
type Order struct {
Type string `json:"type" binding:"required,oneof=physical digital"`
Address string `json:"address" binding:"required_if=Type physical"`
DownloadURL string `json:"download_url" binding:"required_if=Type digital"`
}
r.POST("/order", func(c *gin.Context) {
var order Order
if err := c.ShouldBindJSON(&order); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, order)
})
type Address struct {
Province string `json:"province" binding:"required"`
City string `json:"city" binding:"required"`
Street string `json:"street" binding:"required"`
}
type User struct {
Name string `json:"name" binding:"required"`
Address Address `json:"address" binding:"required"`
}
r.POST("/user", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
})
type CreateOrder struct {
Items []OrderItem `json:"items" binding:"required,min=1,dive"`
}
type OrderItem struct {
ProductID int `json:"product_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gte=1"`
}
r.POST("/order", func(c *gin.Context) {
var order CreateOrder
if err := c.ShouldBindJSON(&order); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, order)
})
func main() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
}
r := gin.Default()
r.Run(":8080")
}
Gin 的验证器功能强大,支持自定义规则、结构体级别验证、条件验证等。自定义错误消息可以提升用户体验。合理使用验证规则可以减少业务代码中的参数检查逻辑。