这是一个很有意思的验证器包,它通过函数式选项模式来避免使用结构体标签和反射,确实提供了一种不同的思路。我来分析一下它的核心实现并给出一个使用示例。
这个包的核心是使用 Field 函数链式地定义验证规则。它通过闭包来捕获字段名和值,然后在 Validate 方法中执行所有规则。
package main
import (
"fmt"
"regexp"
v "github.com/rezakhademix/govalidator"
)
func main() {
// 示例1:基本验证
err := v.Validate(
v.Field("username", "john_doe123",
v.Required(),
v.MinLength(3),
v.MaxLength(20),
v.MatchRegex(regexp.MustCompile(`^[a-zA-Z0-9_]+$`)),
),
v.Field("email", "john@example.com",
v.Required(),
v.IsEmail(),
),
v.Field("age", 25,
v.Required(),
v.Min(18),
v.Max(100),
),
)
if err != nil {
fmt.Printf("验证失败: %v\n", err)
} else {
fmt.Println("验证通过")
}
// 示例2:自定义验证规则
customRule := func(value interface{}) error {
str, ok := value.(string)
if !ok {
return fmt.Errorf("必须是字符串类型")
}
if str != "admin" && str != "user" && str != "guest" {
return fmt.Errorf("角色必须是 admin、user 或 guest 之一")
}
return nil
}
err = v.Validate(
v.Field("role", "admin",
v.Required(),
v.Custom(customRule),
),
)
if err != nil {
fmt.Printf("自定义验证失败: %v\n", err)
}
}
包的内部实现大致是这样的结构:
// 验证器内部实现示意
type Field struct {
name string
value interface{}
rules []Rule
}
type Rule func(interface{}) error
func Field(name string, value interface{}, rules ...Rule) *Field {
return &Field{name: name, value: value, rules: rules}
}
func (f *Field) Validate() error {
for _, rule := range f.rules {
if err := rule(f.value); err != nil {
return fmt.Errorf("%s: %w", f.name, err)
}
}
return nil
}
func Validate(fields ...*Field) error {
for _, field := range fields {
if err := field.Validate(); err != nil {
return err
}
}
return nil
}
// 内置规则示例
func Required() Rule {
return func(value interface{}) error {
if value == nil || value == "" || value == 0 {
return fmt.Errorf("不能为空")
}
return nil
}
}
这种设计模式的优点:
- 编译时安全:不需要运行时反射,所有类型检查在编译时完成
- 灵活的组合:可以轻松组合多个验证规则
- 易于测试:每个验证规则都是独立的函数,易于单元测试
- 类型安全:Go编译器会检查类型是否匹配
不过需要注意的是,由于没有使用反射,对于复杂嵌套结构的验证可能需要更多的手动代码。这个包适合那些希望避免反射开销、需要明确控制验证流程的场景。
从性能角度看,这种实现方式通常比基于反射的验证器更快,特别是在高频调用的场景下。