Go Validator 自定义验证系统详解
本文将深入讲解 Go 语言中的验证器(validator)使用,结合提供的代码示例,展示如何构建一个完整的自定义验证系统。
1. 验证器基础
Go 的 validator
包是一个强大的数据验证工具,常用于验证结构体字段。基本用法如下:
type User struct {
Name string `validate:"required"`
Age int `validate:"min=18"`
}
2. 代码解析
2.1 初始化验证器
initValidator
函数是验证系统的初始化入口:
func initValidator(validate *validator.Validate, trans ut.Translator) *validator.Validate {
// 注册默认中文翻译
_ = zhtranslations.RegisterDefaultTranslations(validate, trans)
// 设置字段名称取自"description"标签
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("description")
return strings.Split(strings.Split(name, ",")[0], ",")[0]
})
// 注册自定义验证规则
for tag, validation := range CustomValidations {
if validation.register != nil {
err := validation.register(tag)
if err != nil {
log.Fatal(gctx.GetInitCtx(), err.Error())
}
}
if validation.trans != nil {
err := validation.trans(tag, trans)
if err != nil {
log.Fatal(gctx.GetInitCtx(), err.Error())
}
}
}
return validate
}
关键点:
- 注册默认中文翻译
- 使用
description
标签作为字段名称(而不是结构体字段名) - 遍历
CustomValidations
注册所有自定义验证规则和对应的翻译
2.2 自定义验证规则
示例中的自定义验证规则定义:
var CustomValidations = map[string]CustomValidation{
"task_priority": {TaskPriorityValidator, TaskPriorityValidationTrans},
}
// 验证器函数
TaskPriorityValidator CustomValidationRegister = func(tag string) error {
return EnumValidateRegister(tag, rpc.Priority_name)
}
// 翻译函数
TaskPriorityValidationTrans CustomValidationTrans = func(tag string, trans ut.Translator) error {
return Validate.RegisterTranslation(tag, trans, func(ut ut.Translator) error {
return ut.Add(tag, EnumTransError(rpc.Priority_name), true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T(tag, fe.Field())
return t
})
}
2.3 结构体验证
在 TaskInfoView
结构体中使用验证标签:
type TaskInfoView struct {
TaskPriority string `json:"taskPriority" description:"任务优先级:1-9,9优先级最高" validate:"required,task_priority"`
}
2.4 验证执行
ValidateStruct
函数封装了验证逻辑:
func ValidateStruct(obj interface{}) error {
err := Validate.Struct(obj)
if err != nil {
var errs validator.ValidationErrors
errors.As(err, &errs)
errMsg := make([]string, 0)
for _, val := range errs.Translate(translate) {
errMsg = append(errMsg, val)
}
return gerror.New(strings.Join(errMsg, ";"))
}
return nil
}
3. 验证流程详解
-
初始化阶段:
- 创建验证器实例
- 注册翻译器
- 配置字段名称来源
- 注册自定义验证规则
-
验证阶段:
- 调用
ValidateStruct
验证结构体 - 验证器检查每个字段的
validate
标签 - 对于
task_priority
字段,会调用我们注册的自定义验证器
- 调用
-
错误处理:
- 收集所有验证错误
- 通过翻译器转换为友好的错误信息
- 合并多个错误信息返回
4. 自定义验证的优势
- 业务规则封装:将业务规则(如优先级范围)封装在验证器中
- 复用性:一次定义,多处使用
- 清晰的错误信息:通过翻译系统提供用户友好的错误提示
- 与结构体解耦:验证规则不直接写在业务代码中
5. 最佳实践
- 将验证规则集中管理(如示例中的
CustomValidations
map) - 为每个自定义验证提供对应的翻译
- 使用有意义的字段描述(description)
- 统一错误处理方式
6. 总结
本文通过实际代码示例展示了如何在 Go 中构建一个完整的自定义验证系统。这种模式特别适合需要复杂业务规则验证的场景,能够保持代码的整洁性和可维护性。
关键要点:
- 验证器初始化配置
- 自定义验证规则定义
- 验证错误翻译处理
- 结构体验证标签使用
- 统一的验证错误处理
通过这种结构化的验证方式,可以大大提高应用程序的数据验证能力和用户体验。