Validator - Go 参数校验库
go-playground/validator 是 Go 最流行的参数校验库,支持丰富的校验规则和自定义验证。掌握 Validator 是开发安全、健壮的 Go Web 应用的基础。
快速开始
📝 基础校验
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Username string `validate:"required,min=3,max=20"`
Email string `validate:"required,email"`
Age int `validate:"required,min=18,max=100"`
Password string `validate:"required,min=8,containsany=!@#$%"`
}
func main() {
validate := validator.New()
user := User{
Username: "alice",
Email: "alice@example.com",
Age: 25,
Password: "pass123!",
}
err := validate.Struct(user)
if err != nil {
// 字段错误
for _, err := range err.(validator.ValidationErrors) {
fmt.Printf("Field: %s, Error: %s\n", err.Field(), err.Tag())
}
return
}
fmt.Println("Validation passed!")
}
💡 Validator 要点
- 单例模式: validate 实例应全局复用
- 并发安全: Validate 是并发安全的
- 缓存: 结构体校验规则会被缓存
- 嵌套校验: 支持嵌套结构体和切片
内置校验规则
📖 常用校验规则
type Request struct {
// 必填
Name string `validate:"required"`
Nickname string `validate:"omitempty,min=2"` // 可选,但有值时需满足
// 字符串
Email string `validate:"email"`
URL string `validate:"url"`
Mobile string `validate:"mobile"`
IP string `validate:"ip"`
UUID string `validate:"uuid"`
// 长度
Code string `validate:"len=6"` // 固定长度
Password string `validate:"min=8,max=20"` // 长度范围
// 数值
Age int `validate:"min=0,max=150"`
Score float64 `validate:"gte=0,lte=100"` // >= 和 <=
Count int `validate:"gt=0"` // >
// 正则
ZipCode string `validate:"regexp=^[0-9]{6}$"`
// 枚举
Status string `validate:"oneof=active inactive pending"`
Role string `validate:"oneof=admin user guest"`
// 时间
Birthday time.Time `validate:"lt"` // 必须小于当前时间
EndTime time.Time `validate:"gtfield=StartTime"` // 必须大于 StartTime 字段
// 切片/数组
Tags []string `validate:"min=1,max=5"`
IDs []int `validate:"unique"` // 元素唯一
// 嵌套
Address *Address `validate:"required,dive"` // dive 深入嵌套
Items []Item `validate:"dive"`
}
type Address struct {
Province string `validate:"required"`
City string `validate:"required"`
District string `validate:"required"`
}
type Item struct {
ID int `validate:"required"`
Name string `validate:"required"`
Price float64 `validate:"gt=0"`
}
错误处理
📝 解析校验错误
func validateUser(user User) map[string]string {
errors := make(map[string]string)
err := validate.Struct(user)
if err == nil {
return errors
}
for _, err := range err.(validator.ValidationErrors) {
field := err.Field()
tag := err.Tag()
switch tag {
case "required":
errors[field] = "不能为空"
case "email":
errors[field] = "邮箱格式不正确"
case "min":
errors[field] = fmt.Sprintf("长度不能小于 %s", err.Param())
case "max":
errors[field] = fmt.Sprintf("长度不能超过 %s", err.Param())
default:
errors[field] = "校验失败"
}
}
return errors
}
自定义验证
📝 注册自定义规则
package main
import (
"github.com/go-playground/validator/v10"
"regexp"
)
var validate *validator.Validate
func init() {
validate = validator.New()
// 自定义规则:校验身份证号
validate.RegisterValidation("id_card", func(fl validator.FieldLevel) bool {
idCard := fl.Field().String()
matched, _ := regexp.MatchString(`^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$`, idCard)
return matched
})
// 自定义规则:校验密码强度
validate.RegisterValidation("password_strength", func(fl validator.FieldLevel) bool {
password := fl.Field().String()
if len(password) < 8 {
return false
}
// 必须包含字母和数字
hasLetter := regexp.MatchString(`[a-zA-Z]`, password)
hasNumber := regexp.MatchString(`[0-9]`, password)
return hasLetter && hasNumber
})
}
type RegisterRequest struct {
IDCard string `validate:"required,id_card"`
Password string `validate:"required,password_strength"`
}
与 Gin 集成
📝 Gin + Validator 中间件
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"net/http"
)
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Errors map[string]string `json:"errors,omitempty"`
}
func ValidationMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取 gin 的 validator
v, _ := c.Get("validator")
validate, ok := v.(*validator.Validate)
if !ok {
c.Next()
return
}
// 获取请求体结构体
if c.Request.Body == nil {
c.Next()
return
}
c.Next()
}
}
func main() {
r := gin.Default()
// 替换 gin 的 validator
if v, ok := r.Validator().Engine().(*validator.Validate); ok {
// 注册自定义规则
v.RegisterValidation("custom_rule", func(fl validator.FieldLevel) bool {
return true
})
}
// 使用示例
r.POST("/users", func(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{
Code: 400,
Message: "Validation failed",
Errors: formatErrors(err),
})
return
}
c.JSON(http.StatusOK, gin.H{"message": "success"})
})
r.Run(":8080")
}
func formatErrors(err error) map[string]string {
errors := make(map[string]string)
for _, e := range err.(validator.ValidationErrors) {
errors[e.Field()] = getErrorMessage(e)
}
return errors
}
最佳实践
✅ Validator 使用建议
- 单例模式: 全局复用 validator 实例
- 自定义错误: 将英文错误转换为中文
- 分组校验: 不同场景使用不同校验组
- 嵌套校验: 使用 dive 深入嵌套结构
- 性能考虑: 避免过度复杂的正则