golang支持多种数据类型的可配置验证插件库ozzo-validation的使用

Golang支持多种数据类型的可配置验证插件库ozzo-validation的使用

ozzo-validation是一个Go包,提供了可配置和可扩展的数据验证功能。它具有以下特点:

  • 使用常规编程结构而不是容易出错的struct标签来指定数据验证方式
  • 可以验证不同类型的数据,如结构体、字符串、字节切片、切片、映射、数组等
  • 只要实现了Validatable接口,就可以验证自定义数据类型
  • 可以验证实现了sql.Valuer接口的数据类型(如sql.NullString)
  • 可定制且格式良好的验证错误
  • 支持错误代码和消息翻译
  • 提供丰富的开箱即用验证规则
  • 创建和使用自定义验证规则非常简单

安装

运行以下命令安装包:

go get github.com/go-ozzo/ozzo-validation

验证简单值

对于简单值(如字符串或整数),可以使用validation.Validate()进行验证。例如:

package main

import (
	"fmt"

	"github.com/go-ozzo/ozzo-validation/v4"
	"github.com/go-ozzo/ozzo-validation/v4/is"
)

func main() {
	data := "example"
	err := validation.Validate(data,
		validation.Required,       // 不能为空
		validation.Length(5, 100), // 长度在5到100之间
		is.URL,                    // 必须是有效的URL
	)
	fmt.Println(err)
	// 输出:
	// must be a valid URL
}

validation.Validate()方法会按列出的顺序运行规则。如果规则失败,方法将返回相应的错误并跳过其余规则。如果值通过所有验证规则,方法将返回nil。

验证结构体

对于结构体值,通常需要检查其字段是否有效。例如,在RESTful应用程序中,可以将请求有效负载解组到结构体中,然后验证结构体字段。可以使用validation.ValidateStruct()来实现这一目的:

type Address struct {
	Street string
	City   string
	State  string
	Zip    string
}

func (a Address) Validate() error {
	return validation.ValidateStruct(&a,
		// Street不能为空,长度必须在5到50之间
		validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
		// City不能为空,长度必须在5到50之间
		validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
		// State不能为空,且必须是由两个大写字母组成的字符串
		validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
		// Zip不能为空,且必须是由五位数字组成的字符串
		validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
	)
}

a := Address{
    Street: "123",
    City:   "Unknown",
    State:  "Virginia",
    Zip:    "12345",
}

err := a.Validate()
fmt.Println(err)
// 输出:
// Street: the length must be between 5 and 50; State: must be in a valid format.

验证映射(Map)

有时可能需要处理存储在映射中的动态数据而不是类型化模型。在这种情况下可以使用validation.Map()

c := map[string]interface{}{
	"Name":  "Qiang Xue",
	"Email": "q",
	"Address": map[string]interface{}{
		"Street": "123",
		"City":   "Unknown",
		"State":  "Virginia",
		"Zip":    "12345",
	},
}

err := validation.Validate(c,
	validation.Map(
		// Name不能为空,长度必须在5到20之间
		validation.Key("Name", validation.Required, validation.Length(5, 20)),
		// Email不能为空,且必须是有效的电子邮件格式
		validation.Key("Email", validation.Required, is.Email),
		// 使用自己的验证规则验证Address
		validation.Key("Address", validation.Map(
			// Street不能为空,长度必须在5到50之间
			validation.Key("Street", validation.Required, validation.Length(5, 50)),
			// City不能为空,长度必须在5到50之间
			validation.Key("City", validation.Required, validation.Length(5, 50)),
			// State不能为空,且必须是由两个大写字母组成的字符串
			validation.Key("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
			// Zip不能为空,且必须是由五位数字组成的字符串
			validation.Key("Zip", validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
		)),
	),
)
fmt.Println(err)
// 输出:
// Address: (State: must be in a valid format; Street: the length must be between 5 and 50.); Email: must be a valid email address.

自定义错误消息

所有内置验证规则都允许自定义错误消息。只需调用规则的Error()方法即可:

data := "2123"
err := validation.Validate(data,
	validation.Required.Error("is required"),
	validation.Match(regexp.MustCompile("^[0-9]{5}$")).Error("must be a string with five digits"),
)
fmt.Println(err)
// 输出:
// must be a string with five digits

创建自定义规则

创建自定义规则非常简单,只需实现validation.Rule接口:

func checkAbc(value interface{}) error {
	s, _ := value.(string)
	if s != "abc" {
		return errors.New("must be abc")
	}
	return nil
}

err := validation.Validate("xyz", validation.By(checkAbc))
fmt.Println(err)
// 输出: must be abc

如果验证函数需要额外参数,可以使用闭包技巧:

func stringEquals(str string) validation.RuleFunc {
	return func(value interface{}) error {
		s, _ := value.(string)
        if s != str {
            return errors.New("unexpected string")
        }
        return nil
    }
}

err := validation.Validate("xyz", validation.By(stringEquals("abc")))
fmt.Println(err)
// 输出: unexpected string

规则组

当多个地方使用多个规则的组合时,可以使用以下技巧创建规则组,使代码更易于维护:

var NameRule = []validation.Rule{
	validation.Required,
	validation.Length(5, 20),
}

type User struct {
	FirstName string
	LastName  string
}

func (u User) Validate() error {
	return validation.ValidateStruct(&u,
		validation.Field(&u.FirstName, NameRule...),
		validation.Field(&u.LastName, NameRule...),
	)
}

内置验证规则

validation包提供以下规则:

  • In(...interface{}): 检查值是否可以在给定的值列表中找到
  • NotIn(...interface{}): 检查值是否不在给定的值列表中
  • Length(min, max int): 检查值的长度是否在指定范围内
  • RuneLength(min, max int): 检查字符串的rune长度是否在指定范围内
  • Min(min interface{})Max(max interface{}): 检查值是否在指定范围内
  • Match(*regexp.Regexp): 检查值是否匹配指定的正则表达式
  • Date(layout string): 检查字符串值是否是具有指定格式的日期
  • Required: 检查值是否不为空(既不是nil也不是零)
  • NotNil: 检查指针值是否为nil
  • NilOrNotEmpty: 检查值是否为nil指针或非空值
  • Nil: 检查值是否为nil指针
  • Empty: 检查值是否为空
  • Skip: 特殊规则,表示应跳过它后面的所有规则
  • MultipleOf: 检查值是否为指定范围的倍数
  • Each(rules ...Rule): 使用其他规则检查可迭代对象(map/slice/array)中的元素
  • When(condition, rules ...Rule): 仅在条件为true时使用指定规则进行验证
  • Else(rules ...Rule): 必须与When(condition, rules ...Rule)一起使用,仅在条件为false时使用指定规则进行验证

is子包提供了一系列常用的字符串验证规则,可以检查值的格式是否满足某些要求。


更多关于golang支持多种数据类型的可配置验证插件库ozzo-validation的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang支持多种数据类型的可配置验证插件库ozzo-validation的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


ozzo-validation:Golang 数据验证库使用指南

ozzo-validation 是一个功能强大且灵活的 Golang 数据验证库,支持多种数据类型的验证。下面我将详细介绍它的使用方法和示例代码。

安装

go get github.com/go-ozzo/ozzo-validation
go get github.com/go-ozzo/ozzo-validation/is

基本使用

1. 简单字段验证

package main

import (
	"fmt"
	"github.com/go-ozzo/ozzo-validation"
	"github.com/go-ozzo/ozzo-validation/is"
)

func main() {
	data := "example@email.com"
	err := validation.Validate(data,
		validation.Required,       // 不能为空
		validation.Length(5, 100), // 长度在5-100之间
		is.Email,                  // 必须是邮箱格式
	)
	
	if err != nil {
		fmt.Println("验证失败:", err)
	} else {
		fmt.Println("验证通过")
	}
}

2. 结构体验证

type User struct {
	Name     string
	Age      int
	Email    string
	Password string
}

func (u User) Validate() error {
	return validation.ValidateStruct(&u,
		// Name不能为空,长度在2-50之间
		validation.Field(&u.Name, validation.Required, validation.Length(2, 50)),
		
		// Age必须大于0,小于150
		validation.Field(&u.Age, validation.Required, validation.Min(0), validation.Max(150)),
		
		// Email必须符合邮箱格式
		validation.Field(&u.Email, validation.Required, is.Email),
		
		// Password长度至少6个字符
		validation.Field(&u.Password, validation.Required, validation.Length(6, 0)),
	)
}

func main() {
	user := User{
		Name:     "John",
		Age:      30,
		Email:    "john@example.com",
		Password: "123456",
	}
	
	if err := user.Validate(); err != nil {
		fmt.Println("验证失败:", err)
	} else {
		fmt.Println("验证通过")
	}
}

高级功能

1. 自定义验证规则

func isAdmin(value interface{}) error {
	s, _ := value.(string)
	if s != "admin" {
		return fmt.Errorf("必须是admin")
	}
	return nil
}

func main() {
	username := "user"
	err := validation.Validate(username,
		validation.Required,
		validation.By(isAdmin),
	)
	
	if err != nil {
		fmt.Println("验证失败:", err)
	}
}

2. 条件验证

type Order struct {
	PaymentMethod string
	CreditCard    string
}

func (o Order) Validate() error {
	return validation.ValidateStruct(&o,
		validation.Field(&o.PaymentMethod, validation.Required, validation.In("credit", "paypal")),
		
		// 只有当PaymentMethod为credit时才验证CreditCard
		validation.Field(&o.CreditCard,
			validation.When(o.PaymentMethod == "credit", validation.Required, validation.Length(16, 16)),
		),
	)
}

3. 切片验证

func main() {
	names := []string{"Alice", "Bob", ""}
	
	err := validation.Validate(names,
		validation.Required,              // 切片本身不能为nil
		validation.Length(1, 10),         // 切片长度限制
		validation.Each(                  // 对每个元素验证
			validation.Required,          // 每个元素不能为空
			validation.Length(2, 50),     // 每个元素长度限制
		),
	)
	
	if err != nil {
		fmt.Println("验证失败:", err)
	}
}

4. Map验证

func main() {
	data := map[string]interface{}{
		"name":  "John",
		"age":   30,
		"email": "john@example.com",
	}
	
	rules := validation.Map(
		validation.Key("name", validation.Required, validation.Length(2, 50)),
		validation.Key("age", validation.Required, validation.Min(0), validation.Max(150)),
		validation.Key("email", validation.Required, is.Email),
	)
	
	err := validation.Validate(data, rules)
	
	if err != nil {
		fmt.Println("验证失败:", err)
	}
}

内置验证器

ozzo-validation 提供了丰富的内置验证器:

  • 通用验证器:

    • Required - 值不能为零值
    • NotNil - 值不能为nil
    • In - 值必须在指定范围内
    • Match - 值必须匹配正则表达式
  • 字符串验证器:

    • Length - 长度限制
    • RuneLength - Unicode字符长度限制
    • Date - 必须是日期格式
  • 数值验证器:

    • Min - 最小值
    • Max - 最大值
    • Range - 数值范围
  • is 包提供的验证器:

    • is.Email - 邮箱格式
    • is.URL - URL格式
    • is.IPv4 - IPv4地址
    • is.HexColor - 十六进制颜色代码

错误处理

func main() {
	user := User{
		Name:     "", // 空名字
		Age:      200, // 年龄过大
		Email:    "invalid-email",
		Password: "123",
	}
	
	if err := user.Validate(); err != nil {
		if e, ok := err.(validation.Errors); ok {
			for field, fieldErr := range e {
				fmt.Printf("%s: %v\n", field, fieldErr)
			}
		} else {
			fmt.Println("验证失败:", err)
		}
	}
}

ozzo-validation 是一个功能全面且灵活的验证库,适用于各种数据验证场景。通过组合内置验证器和自定义验证规则,可以轻松实现复杂的验证逻辑。

回到顶部