数据验证
数据验证
go-playground/validator 使用结构体标签声明验证规则,支持字段、跨字段、嵌套结构体、集合和自定义验证。
go get github.com/go-playground/validator/v10
结构体校验
type CreateUserRequest struct {
Name string `validate:"required,min=2,max=50"`
Email string `validate:"required,email"`
Password string `validate:"required,min=8"`
Age int `validate:"gte=18,lte=120"`
Roles []string `validate:"min=1,dive,oneof=admin editor viewer"`
}
validate := validator.New(validator.WithRequiredStructEnabled())
request := CreateUserRequest{
Name: "Alice",
Email: "alice@example.com",
Age: 20,
Roles: []string{"editor"},
}
err := validate.Struct(request)
dive 会进入 Slice、Array 或 Map,继续校验每个元素。验证器实例会缓存结构体信息,应在应用中复用,而不是每次请求都重新创建。
处理错误
验证失败时可以把错误转换为 validator.ValidationErrors,再映射为稳定的 API 错误格式:
var validationErrors validator.ValidationErrors
if errors.As(err, &validationErrors) {
for _, fieldError := range validationErrors {
fmt.Printf("field=%s rule=%s param=%s\n",
fieldError.Field(),
fieldError.Tag(),
fieldError.Param(),
)
}
}
不要把内部结构体名称和未经处理的错误文本直接返回给客户端。可以为字段名和规则建立翻译表,统一输出错误码与用户可读消息。
跨字段与自定义规则
确认密码可以使用内置的跨字段规则:
type RegisterRequest struct {
Password string `validate:"required,min=8"`
ConfirmPassword string `validate:"required,eqfield=Password"`
}
业务特有规则可以注册为自定义标签:
validate.RegisterValidation("username", func(fl validator.FieldLevel) bool {
value := fl.Field().String()
return usernamePattern.MatchString(value)
})
type Request struct {
Username string `validate:"required,username"`
}
自定义验证函数应保持纯粹和快速。需要查询数据库的唯一性等业务规则,应放在 Service 层处理,而不是塞进字段验证器。
实践建议
- 验证外部输入,但不要重复验证由程序内部构造且已受类型约束的数据。
- 标签适合字段形状校验;涉及权限、状态流转和数据库查询的规则属于业务逻辑。
- Gin 默认集成了 validator,但独立创建实例时需要自行注册自定义规则和翻译器。
- 校验通过不代表输入安全,SQL 参数化、HTML 转义和文件类型检查仍需分别处理。