自定义验证 #
一、自定义验证器 #
1.1 注册验证器 #
go
import "github.com/go-playground/validator/v10"
validate := validator.New()
validate.RegisterValidation("custom", func(fl validator.FieldLevel) bool {
// 验证逻辑
return true
})
1.2 验证器函数签名 #
go
type Func func(fl FieldLevel) bool
type FieldLevel interface {
Field() reflect.Value // 当前字段值
FieldName() string // 字段名称
StructFieldName() string // 结构体字段名
Param() string // 验证参数
GetStructFieldOK() (reflect.Value, bool) // 获取其他字段
Parent() reflect.Value // 父结构体
}
二、常用自定义验证器 #
2.1 中国手机号验证 #
go
validate.RegisterValidation("cn_phone", func(fl validator.FieldLevel) bool {
phone := fl.Field().String()
matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
return matched
})
// 使用
type User struct {
Phone string `validate:"required,cn_phone"`
}
2.2 身份证号验证 #
go
validate.RegisterValidation("id_card", func(fl validator.FieldLevel) bool {
idCard := fl.Field().String()
// 18位身份证
if len(idCard) != 18 {
return false
}
// 校验位
weights := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
checkCodes := "10X98765432"
sum := 0
for i := 0; i < 17; i++ {
num, err := strconv.Atoi(string(idCard[i]))
if err != nil {
return false
}
sum += num * weights[i]
}
checkCode := checkCodes[sum%11]
return strings.ToUpper(string(idCard[17])) == string(checkCode)
})
2.3 密码强度验证 #
go
validate.RegisterValidation("strong_password", func(fl validator.FieldLevel) bool {
password := fl.Field().String()
if len(password) < 8 {
return false
}
hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
hasNumber := regexp.MustCompile(`[0-9]`).MatchString(password)
hasSpecial := regexp.MustCompile(`[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]`).MatchString(password)
count := 0
if hasUpper { count++ }
if hasLower { count++ }
if hasNumber { count++ }
if hasSpecial { count++ }
return count >= 3
})
2.4 日期验证 #
go
validate.RegisterValidation("date_format", func(fl validator.FieldLevel) bool {
dateStr := fl.Field().String()
format := fl.Param()
_, err := time.Parse(format, dateStr)
return err == nil
})
// 使用
type Event struct {
Date string `validate:"required,date_format=2006-01-02"`
}
2.5 枚举值验证 #
go
validate.RegisterValidation("enum", func(fl validator.FieldLevel) bool {
value := fl.Field().String()
allowed := strings.Split(fl.Param(), ",")
for _, v := range allowed {
if value == v {
return true
}
}
return false
})
// 使用
type Status struct {
Value string `validate:"required,enum=active,inactive,pending"`
}
三、跨字段验证 #
3.1 字段比较验证 #
go
validate.RegisterValidation("gt_field", func(fl validator.FieldLevel) bool {
field := fl.Field()
otherField, _, ok := fl.GetStructFieldOK(fl.Param())
if !ok {
return false
}
switch field.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() > otherField.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return field.Uint() > otherField.Uint()
case reflect.Float32, reflect.Float64:
return field.Float() > otherField.Float()
}
return false
})
// 使用
type Range struct {
Min int `validate:"required"`
Max int `validate:"required,gt_field=Min"`
}
3.2 条件验证 #
go
validate.RegisterValidation("required_if", func(fl validator.FieldLevel) bool {
param := fl.Param()
parts := strings.Split(param, "=")
if len(parts) != 2 {
return false
}
otherField, _, ok := fl.GetStructFieldOK(parts[0])
if !ok {
return false
}
if fmt.Sprintf("%v", otherField.Interface()) == parts[1] {
return fl.Field().Interface() != reflect.Zero(fl.Field().Type()).Interface()
}
return true
})
// 使用
type Order struct {
PaymentMethod string `validate:"required"`
CardNumber string `validate:"required_if=PaymentMethod=card"`
}
四、数据库验证 #
4.1 唯一性验证 #
go
func UniqueValidator(db *gorm.DB) validator.Func {
return func(fl validator.FieldLevel) bool {
param := fl.Param()
parts := strings.Split(param, ",")
if len(parts) != 2 {
return false
}
table := parts[0]
column := parts[1]
value := fl.Field().String()
var count int64
db.Table(table).Where(column+" = ?", value).Count(&count)
return count == 0
}
}
// 注册
validate.RegisterValidation("unique", UniqueValidator(db))
// 使用
type User struct {
Email string `validate:"required,email,unique=users,email"`
}
4.2 存在性验证 #
go
func ExistsValidator(db *gorm.DB) validator.Func {
return func(fl validator.FieldLevel) bool {
param := fl.Param()
parts := strings.Split(param, ",")
if len(parts) != 2 {
return false
}
table := parts[0]
column := parts[1]
value := fl.Field().Interface()
var count int64
db.Table(table).Where(column+" = ?", value).Count(&count)
return count > 0
}
}
// 使用
type Post struct {
UserID uint `validate:"required,exists=users,id"`
}
五、验证器封装 #
5.1 验证器结构体 #
go
type Validator struct {
validate *validator.Validate
}
func NewValidator() *Validator {
v := validator.New()
// 注册自定义验证器
v.RegisterValidation("cn_phone", validateCNPhone)
v.RegisterValidation("id_card", validateIDCard)
v.RegisterValidation("strong_password", validateStrongPassword)
return &Validator{validate: v}
}
func (v *Validator) Validate(s interface{}) error {
return v.validate.Struct(s)
}
func (v *Validator) ValidateVar(field interface{}, tag string) error {
return v.validate.Var(field, tag)
}
5.2 使用封装 #
go
var validatorInstance = NewValidator()
func ValidateBody(s interface{}) fiber.Handler {
return func(c *fiber.Ctx) error {
value := reflect.New(reflect.TypeOf(s).Elem()).Interface()
if err := c.BodyParser(value); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Invalid request body",
})
}
if err := validatorInstance.Validate(value); err != nil {
return c.Status(400).JSON(fiber.Map{
"error": "Validation failed",
"fields": formatErrors(err),
})
}
c.Locals("validated", value)
return c.Next()
}
}
六、总结 #
6.1 自定义验证要点 #
| 要点 | 说明 |
|---|---|
| 注册验证器 | validate.RegisterValidation() |
| 字段访问 | fl.Field() |
| 参数获取 | fl.Param() |
| 跨字段 | fl.GetStructFieldOK() |
6.2 下一步 #
现在你已经掌握了自定义验证,接下来让我们学习 模板引擎配置,了解Fiber的模板渲染功能!
最后更新:2026-03-28