自定义验证 #

一、自定义验证器 #

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