Golang验证器新包发布:无需结构标签!

Golang验证器新包发布:无需结构标签! 大家好!

最近我发布了一个新的 Go 验证器包,名为 GoValidator

GitHub: https://github.com/rezakhademix/govalidator

它具有以下一些特性:

  1. 无需结构体标签
  2. 无需类型反射
  3. 您可以定义任何自定义规则
  4. 超级轻量!

它将帮助您轻松处理项目数据清理工作。

如果您有兴趣帮助我改进 GoValidator 包,我将非常高兴,并且欢迎您对这个包提出意见。

4 回复

感谢您的推荐和宝贵时间。我一定会认真考虑。目前它还只是一个雏形,但很快就会成长起来!

更多关于Golang验证器新包发布:无需结构标签!的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


最近我发布了一个名为 GoValidator 的新 Golang 验证器包。

Github: https://github.com/rezakhademix/govalidator

它有一些特性,例如:

  1. 无需结构体标签
  2. 无需类型反射
  3. 您可以定义任何自定义规则
  4. 超级轻量!

它将帮助您轻松处理项目数据清洗。

如果您有兴趣帮助我改进 GoValidator 包,我将非常高兴,也欢迎您对这个包提出意见。

轻量级是一个优势,特别是对于性能至关重要的项目。

可以通过将自定义错误消息设为可选来简化语法。例如:v.MaxInt(v.Name, "First Name", 20).WithErrMessage("my custom message") 这样可以使调用变得简洁,避免到处都是这些不必要的空字符串。

另一个重要的情况是国际化(i21n)。如果你真的想为最终用户生成错误消息,这些消息应该易于翻译。这可以通过不同的方式实现:一种方式是使用包含替换语法的类型化错误。 或者,你可以导出一个方法,允许提供一个 map[语言]错误消息 和当前语言,这样错误消息就可以根据匹配的语言代码从映射中获取。

v.MaxInt(v.Name, "First Name", 20).WithErrMessage("my custom message")

这是一个很有意思的验证器包,它通过函数式选项模式来避免使用结构体标签和反射,确实提供了一种不同的思路。我来分析一下它的核心实现并给出一个使用示例。

这个包的核心是使用 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
    }
}

这种设计模式的优点:

  1. 编译时安全:不需要运行时反射,所有类型检查在编译时完成
  2. 灵活的组合:可以轻松组合多个验证规则
  3. 易于测试:每个验证规则都是独立的函数,易于单元测试
  4. 类型安全:Go编译器会检查类型是否匹配

不过需要注意的是,由于没有使用反射,对于复杂嵌套结构的验证可能需要更多的手动代码。这个包适合那些希望避免反射开销、需要明确控制验证流程的场景。

从性能角度看,这种实现方式通常比基于反射的验证器更快,特别是在高频调用的场景下。

回到顶部