golang运行时值解析与验证的Zod风格模式构建插件Zog的使用
Golang运行时值解析与验证的Zod风格模式构建插件Zog的使用
Zog是一个用于运行时值解析和验证的Golang库,类似于Zod和Yup的Schema解析器和验证器。它提供了简洁而富有表现力的API,能够处理从简单到复杂的数据模型验证。
主要特性
- 简洁而富有表现力的Schema接口
- 类似Zod的API,使用方法链构建类型安全的Schema
- 可扩展:可以添加自定义的测试和Schema
- 丰富的错误信息,便于调试
- 高性能:Zog是Go中最快的验证库之一
- 内置大多数类型的强制转换支持
- 零依赖
- 提供四个辅助包:zenv、zhttp、zjson和i18n
安装
go get github.com/Oudwins/zog
完整示例
1. 创建用户Schema和结构体
import (
z "github.com/Oudwins/zog"
)
type User struct {
Name string
Age int
}
var userSchema = z.Struct(z.Shape{
// 非常重要:Schema键(如"name")必须匹配结构体字段名,而不是输入数据
"name": z.String().Min(3, z.Message("Override default message")).Max(10),
"age": z.Int().GT(18),
})
2. 使用schema.Parse()验证
func main() {
u := User{}
m := map[string]string{
"name": "Zog",
"age": "", // 不会返回错误,因为字段默认是可选的
}
errsMap := userSchema.Parse(m, &u)
if errsMap != nil {
// 处理错误 -> 参见错误部分
}
u.Name // "Zog"
// 注意:这看起来可能有点奇怪,但我们没有说age是必填的,
// 所以Zog只是跳过了空字符串,我们得到了未初始化的int
// 如果我们希望0是age的有效值,可以使用int指针,如果输入数据中没有值,指针将为nil
u.Age // 0
}
3. 使用schema.Validate()验证
func main() {
u := User{
Name: "Zog",
Age: 0, // 不会返回错误,因为字段默认是可选的否则会报错
}
errsMap := userSchema.Validate(&u)
if errsMap != nil {
// 处理错误 -> 参见错误部分
}
}
4. 与HTTP和JSON一起使用
使用zhttp包处理JSON、表单和查询参数:
import (
zhttp "github.com/Oudwins/zog/zhttp"
)
err := userSchema.Parse(zhttp.Request(r), &user)
使用zjson包处理JSON数据:
import (
zjson "github.com/Oudwins/zog/zjson"
)
err := userSchema.Parse(zjson.Decode(bytes.NewReader(jsonBytes)), &user)
5. 验证环境变量
使用zenv包验证环境变量:
import (
zenv "github.com/Oudwins/zog/zenv"
)
err := envSchema.Parse(zenv.NewDataProvider(), &envs)
6. 解析单个字段
var t = time.Time
errsList := Time().Required().Parse("2020-01-01T00:00:00Z", &t)
7. 无限制地转换数据
var dest []string
schema := z.Preprocess(func(data any, ctx z.Ctx) ([]string, error) {
s := data.(string) // 不要这样做,实际上应该检查类型
return strings.Split(s, ","), nil
}, z.Slice(z.String().Trim().Email().Required()))
errs := schema.Parse("foo@bar.com,bar@foo.com", &dest) // dest = [foo@bar.com bar@foo.com]
路线图
- 支持schema.Clone()
- 支持structs和slices的catch和default
- 从Schema生成结构体
许可证
该项目采用MIT许可证 - 详情请参见LICENSE文件。
更多关于golang运行时值解析与验证的Zod风格模式构建插件Zog的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang运行时值解析与验证的Zod风格模式构建插件Zog的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang运行时值解析与验证的Zod风格插件:Zog
在JavaScript生态中,Zod是一个非常流行的运行时类型验证库。在Go语言中,我们可以构建类似的运行时值解析与验证工具。下面我将介绍如何实现一个类似Zod风格的Go插件"Zog"。
Zog核心设计
Zog的设计目标是提供一种声明式、链式调用的API来定义和验证数据结构,类似于Zod的风格:
package zog
type Schema[T any] interface {
Parse(data interface{}) (T, error)
// 其他验证方法...
}
基本类型验证
首先实现基础类型的验证器:
func String() *StringSchema {
return &StringSchema{}
}
type StringSchema struct {
checks []func(string) error
}
func (s *StringSchema) Parse(data interface{}) (string, error) {
str, ok := data.(string)
if !ok {
return "", fmt.Errorf("expected string, got %T", data)
}
for _, check := range s.checks {
if err := check(str); err != nil {
return "", err
}
}
return str, nil
}
func (s *StringSchema) Min(length int) *StringSchema {
s.checks = append(s.checks, func(str string) error {
if len(str) < length {
return fmt.Errorf("string must be at least %d characters", length)
}
return nil
})
return s
}
func (s *StringSchema) Max(length int) *StringSchema {
s.checks = append(s.checks, func(str string) error {
if len(str) > length {
return fmt.Errorf("string must be at most %d characters", length)
}
return nil
})
return s
}
对象结构验证
实现类似Zod的对象结构验证:
func Object[T any](shape map[string]Schema) *ObjectSchema[T] {
return &ObjectSchema[T]{
shape: shape,
}
}
type ObjectSchema[T any] struct {
shape map[string]Schema
}
func (s *ObjectSchema[T]) Parse(data interface{}) (T, error) {
var result T
m, ok := data.(map[string]interface{})
if !ok {
return result, fmt.Errorf("expected object, got %T", data)
}
// 使用反射构建结果
v := reflect.ValueOf(&result).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
key := field.Tag.Get("json")
if key == "" {
key = field.Name
}
if schema, ok := s.shape[key]; ok {
val, err := schema.Parse(m[key])
if err != nil {
return result, fmt.Errorf("field %s: %w", key, err)
}
fv := v.Field(i)
if fv.CanSet() {
fv.Set(reflect.ValueOf(val))
}
}
}
return result, nil
}
使用示例
下面是如何使用Zog进行验证的示例:
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
}
userSchema := zog.Object[User](map[string]zog.Schema{
"name": zog.String().Min(3).Max(50),
"age": zog.Number().Min(18).Max(120),
"email": zog.String().Email(),
})
func HandleRequest(data []byte) error {
var input map[string]interface{}
if err := json.Unmarshal(data, &input); err != nil {
return err
}
user, err := userSchema.Parse(input)
if err != nil {
return fmt.Errorf("validation error: %w", err)
}
// 使用验证后的user...
return nil
}
高级特性实现
- 自定义验证器:
func (s *StringSchema) Custom(fn func(string) error) *StringSchema {
s.checks = append(s.checks, fn)
return s
}
// 使用示例
zog.String().Custom(func(s string) error {
if s != strings.ToLower(s) {
return errors.New("must be lowercase")
}
return nil
})
- 联合类型:
func Union(schemas ...Schema) *UnionSchema {
return &UnionSchema{schemas: schemas}
}
type UnionSchema struct {
schemas []Schema
}
func (s *UnionSchema) Parse(data interface{}) (interface{}, error) {
var lastErr error
for _, schema := range s.schemas {
if val, err := schema.Parse(data); err == nil {
return val, nil
} else {
lastErr = err
}
}
return nil, fmt.Errorf("no matching schema found: %w", lastErr)
}
性能考虑
在Go中实现运行时验证需要注意性能:
- 使用缓存反射类型信息
- 避免不必要的内存分配
- 提供编译时验证选项(如代码生成)
总结
通过构建Zog这样的库,我们可以在Go中获得类似Zod的开发体验:
- 声明式的模式定义
- 链式API调用
- 丰富的内置验证器
- 类型安全的解析结果
- 清晰的错误信息
虽然Go是静态类型语言,但这样的运行时验证在处理外部输入(如JSON、表单数据)时仍然非常有用,可以提供更好的错误处理和更健壮的系统边界。
完整的实现需要考虑更多细节,如嵌套对象、数组验证、可选字段等,但以上代码已经展示了核心概念。