validator

package
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2026 License: BSD-2-Clause Imports: 6 Imported by: 0

README

Validator

基于 Gin 框架的数据验证器,使用 go-playground/validator 提供高效的结构体验证功能。

特性

  • 结构体验证 - 基于结构体标签的声明式验证,简洁直观
  • 自定义验证器 - 支持注册自定义验证函数,灵活扩展验证规则
  • 自动错误格式化 - 友好的错误提示信息,使用 JSON 标签作为字段名
  • 密码复杂度验证 - 内置密码强度验证器,可自定义复杂度要求
  • 泛型支持 - 提供泛型辅助函数,简化绑定和验证流程
  • Gin 集成 - 无缝集成 Gin 框架,自动处理 JSON 绑定

快速开始

基本使用
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/lite-lake/litecore-go/util/validator"
)

// 定义请求结构体
type LoginRequest struct {
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8"`
}

func main() {
    r := gin.Default()

    // 创建验证器实例
    v := validator.NewDefaultValidator()

    r.POST("/login", func(c *gin.Context) {
        var req LoginRequest

        // 验证请求
        if err := v.Validate(c, &req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "error": err.Error(),
            })
            return
        }

        // 处理登录逻辑
        c.JSON(http.StatusOK, gin.H{
            "message": "Login successful",
            "email":   req.Email,
        })
    })

    r.Run(":8080")
}
使用泛型辅助函数
r.POST("/register", func(c *gin.Context) {
    // 使用泛型函数一步完成绑定和验证
    req, err := validator.BindAndValidate[RegisterRequest](c, v)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // req 已经是 *RegisterRequest 类型
    c.JSON(http.StatusOK, gin.H{
        "message": "Registration successful",
        "email":   req.Email,
    })
})

功能详解

数据验证

使用 Validate 方法验证请求数据:

type CreateUserRequest struct {
    Name     string `json:"name" validate:"required,min=2,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8,max=100"`
    Age      int    `json:"age" validate:"required,gte=18,lte=120"`
}

func CreateUser(c *gin.Context) {
    v := validator.NewDefaultValidator()
    var req CreateUserRequest

    if err := v.Validate(c, &req); err != nil {
        // 错误信息会自动格式化,例如:
        // "name is required; email must be a valid email; password must be at least 8 characters"
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    // 处理业务逻辑
}
常用验证标签
type ProductRequest struct {
    // 必填字段
    Name  string `json:"name" validate:"required"`

    // 字符串长度验证
    SKU   string `json:"sku" validate:"min=3,max=20"`

    // 邮箱验证
    Email string `json:"email" validate:"email"`

    // 数值范围验证
    Price float64 `json:"price" validate:"gt=0"`
    Qty   int     `json:"qty" validate:"gte=1,lte=100"`

    // 可选字段(为空时跳过验证)
    Phone string `json:"phone,omitempty" validate:"omitempty,len=11"`

    // 枚举验证
    Status string `json:"status" validate:"oneof=active inactive pending"`
}
嵌套结构体验证
type Address struct {
    Street  string `json:"street" validate:"required"`
    City    string `json:"city" validate:"required"`
    ZipCode string `json:"zip_code" validate:"required,len=6"`
}

type CreateUserRequest struct {
    Name    string  `json:"name" validate:"required"`
    Address Address `json:"address" validate:"required"` // 嵌套验证
}

func CreateUser(c *gin.Context) {
    v := validator.NewDefaultValidator()
    var req CreateUserRequest

    if err := v.Validate(c, &req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
}
切片和数组验证
type BatchDeleteRequest struct {
    IDs []int `json:"ids" validate:"required,min=1,dive,gte=1"`
}

// dive 标签表示对切片中的每个元素进行验证
func BatchDelete(c *gin.Context) {
    v := validator.NewDefaultValidator()
    var req BatchDeleteRequest

    if err := v.Validate(c, &req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
}
自定义验证器

注册自定义验证函数:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "github.com/lite-lake/litecore-go/util/validator"
)

// 自定义验证函数 - 验证用户名只包含小写字母和数字
func validateUsername(fl validator.FieldLevel) bool {
    username := fl.Field().String()
    for _, c := range username {
        if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
            return false
        }
    }
    return true
}

func main() {
    r := gin.Default()

    // 创建验证器
    v := validator.NewDefaultValidator()

    // 注册自定义验证器
    v.RegisterValidation("username", validateUsername)

    type RegisterRequest struct {
        Username string `json:"username" validate:"required,username,min=4,max=20"`
        Email    string `json:"email" validate:"required,email"`
    }

    r.POST("/register", func(c *gin.Context) {
        var req RegisterRequest
        if err := v.Validate(c, &req); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        c.JSON(200, gin.H{"message": "success"})
    })

    r.Run(":8080")
}
密码复杂度验证

使用内置的密码验证器:

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/lite-lake/litecore-go/util/validator"
)

func main() {
    r := gin.Default()

    // 创建验证器
    v := validator.NewDefaultValidator()

    // 注册默认的密码复杂度验证器
    // 要求:至少12位,包含大小写字母、数字和特殊字符
    validator.RegisterPasswordValidation(v)

    type RegisterRequest struct {
        Email    string `json:"email" validate:"required,email"`
        Password string `json:"password" validate:"required,complexPassword"`
    }

    r.POST("/register", func(c *gin.Context) {
        var req RegisterRequest
        if err := v.Validate(c, &req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "error": err.Error(),
                // 如果密码不符合要求,错误信息示例:
                // "password must contain: at least 12 characters, uppercase letter,
                //  lowercase letter, number, special character"
            })
            return
        }

        c.JSON(http.StatusOK, gin.H{"message": "Registration successful"})
    })

    r.Run(":8080")
}
自定义密码复杂度要求
// 创建自定义密码配置
customConfig := &validator.PasswordConfig{
    MinLength:      8,   // 最小8位
    MaxLength:      64,  // 最大64位
    RequireUpper:   true, // 要求大写
    RequireLower:   true, // 要求小写
    RequireNumber:  true, // 要求数字
    RequireSpecial: false, // 不要求特殊字符
}

// 使用自定义配置注册验证器
validator.RegisterPasswordValidationWithConfig(v, customConfig)

type RegisterRequest struct {
    Password string `json:"password" validate:"required,complexPassword"`
}
获取密码要求说明
// 获取默认密码要求
requirements := validator.GetPasswordRequirements()
// 返回: "Password must contain: at least 12 characters, uppercase letter,
//        lowercase letter, number, special character"

// 或使用自定义配置
requirements := validator.GetPasswordRequirementsWithConfig(customConfig)

// 在 API 响应中返回密码要求
c.JSON(200, gin.H{
    "password_requirements": requirements,
})
服务层密码验证
import "github.com/lite-lake/litecore-go/util/validator"

func CreateUserService(email, password string) error {
    // 在服务层验证密码复杂度
    if err := validator.ValidatePassword(password, validator.DefaultPasswordConfig()); err != nil {
        return fmt.Errorf("invalid password: %w", err)
    }

    // 创建用户...
    return nil
}
错误处理

验证器会返回友好的错误信息:

type ValidationError struct {
    Message string
    Errors  validator.ValidationErrors
}

func handleRequest(c *gin.Context) {
    v := validator.NewDefaultValidator()
    var req CreateUserRequest

    if err := v.Validate(c, &req); err != nil {
        // 类型断言获取详细错误信息
        if ve, ok := err.(*validator.ValidationError); ok {
            c.JSON(400, gin.H{
                "error":   ve.Message,
                "details": ve.Errors, // 原始验证错误
            })
            return
        }

        // 其他错误(如 JSON 解析错误)
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    // 处理成功...
}

错误信息示例:

// 单个字段错误
{
  "error": "email is required"
}

// 多个字段错误
{
  "error": "name must be at least 2 characters; email must be a valid email; password must be at least 8 characters"
}

// 密码复杂度错误
{
  "error": "password must contain: at least 12 characters, uppercase letter, lowercase letter, number and special character"
}

API 参考

接口和类型
Validator 接口
type Validator interface {
    Validate(ctx *gin.Context, obj interface{}) error
}
DefaultValidator

默认验证器实现。

type DefaultValidator struct {
    engine *validator.Validate
}
核心函数
NewDefaultValidator

创建默认验证器实例。

func NewDefaultValidator() *DefaultValidator

示例:

v := validator.NewDefaultValidator()
Validate

验证并绑定请求数据到结构体。

func (v *DefaultValidator) Validate(ctx *gin.Context, obj interface{}) error

参数:

  • ctx - Gin 上下文
  • obj - 指向结构体的指针

返回:

  • error - 验证错误,成功时返回 nil

示例:

var req LoginRequest
if err := v.Validate(c, &req); err != nil {
    return err
}
RegisterValidation

注册自定义验证函数。

func (v *DefaultValidator) RegisterValidation(tag string, fn validator.Func) error

参数:

  • tag - 验证标签名(如 "complexPassword")
  • fn - 验证函数,签名为 func(fl validator.FieldLevel) bool

返回:

  • error - 注册错误

示例:

err := v.RegisterValidation("even", func(fl validator.FieldLevel) bool {
    value := fl.Field().Int()
    return value%2 == 0
})
BindAndValidate

泛型辅助函数,一步完成绑定和验证。

func BindAndValidate[T any](ctx *gin.Context, validator Validator) (*T, error)

类型参数:

  • T - 请求结构体类型

参数:

  • ctx - Gin 上下文
  • validator - 验证器实例

返回:

  • *T - 验证后的请求结构体指针
  • error - 验证错误

示例:

req, err := validator.BindAndValidate[CreateUserRequest](c, v)
if err != nil {
    return err
}
// req 是 *CreateUserRequest 类型
密码验证相关
PasswordConfig

密码验证配置结构体。

type PasswordConfig struct {
    MinLength      int  // 密码最小长度(默认 12)
    MaxLength      int  // 密码最大长度(默认 128)
    RequireUpper   bool // 是否要求大写字母(默认 true)
    RequireLower   bool // 是否要求小写字母(默认 true)
    RequireNumber  bool // 是否要求数字(默认 true)
    RequireSpecial bool // 是否要求特殊字符(默认 true)
}
DefaultPasswordConfig

返回默认密码配置。

func DefaultPasswordConfig() *PasswordConfig
ValidatePassword

使用指定配置验证密码复杂度。

func ValidatePassword(password string, config *PasswordConfig) error

参数:

  • password: 待验证的密码
  • config: 密码配置,为 nil 时使用默认配置

示例:

err := validator.ValidatePassword("MyPassword123!", validator.DefaultPasswordConfig())
RegisterPasswordValidation

使用默认配置注册密码复杂度验证器。

func RegisterPasswordValidation(v *DefaultValidator) error
RegisterPasswordValidationWithConfig

使用自定义配置注册密码验证器。

func RegisterPasswordValidationWithConfig(v *DefaultValidator, config *PasswordConfig) error
GetPasswordRequirements

获取默认密码要求说明。

func GetPasswordRequirements() string

返回值示例:

"Password must contain: at least 12 characters, uppercase letter, lowercase letter, number, special character"
GetPasswordRequirementsWithConfig

使用自定义配置获取密码要求说明。

func GetPasswordRequirementsWithConfig(config *PasswordConfig) string

常用验证标签

字符串验证
标签 说明 示例
required 必填字段 validate:"required"
min=n 最小长度为 n validate:"min=8"
max=n 最大长度为 n validate:"max=100"
len=n 长度必须为 n validate:"len=11"
email 有效的邮箱地址 validate:"email"
url 有效的 URL validate:"url"
startswith=abc 以指定前缀开头 validate:"startswith=user_"
endswith=xyz 以指定后缀结尾 validate:"endswith=@example.com"
数值验证
标签 说明 示例
gt=n 大于 n validate:"gt=0"
gte=n 大于等于 n validate:"gte=18"
lt=n 小于 n validate:"lt=100"
lte=n 小于等于 n validate:"lte=120"
eq=n 等于 n validate:"eq=10"
ne=n 不等于 n validate:"ne=0"
其他验证
标签 说明 示例
oneof=a b c 必须是指定值之一 validate:"oneof=active inactive"
required_unless=Field=value 除非指定字段等于值,否则必填 validate:"required_unless=Type=guest"
required_with=Field 当指定字段存在时必填 validate:"required_with=Phone"
required_without=Field 当指定字段不存在时必填 validate:"required_without=Email"
omitempty 为空时跳过验证 validate:"omitempty,email"
dive 对数组/切片/map 的元素进行验证 validate:"dive,required"

完整示例

用户注册 API
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/lite-lake/litecore-go/util/validator"
)

type RegisterRequest struct {
    Username string `json:"username" validate:"required,alphanum,min=4,max=20"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,complexPassword"`
    Age      int    `json:"age" validate:"required,gte=18,lte=120"`
}

type Address struct {
    Province string `json:"province" validate:"required"`
    City     string `json:"city" validate:"required"`
    Detail   string `json:"detail" validate:"required,max=200"`
}

type CompleteProfileRequest struct {
    UserID  int64   `json:"user_id" validate:"required"`
    Address Address `json:"address" validate:"required"`
}

func main() {
    r := gin.Default()

    // 初始化验证器
    v := validator.NewDefaultValidator()

    // 注册密码复杂度验证器
    if err := validator.RegisterPasswordValidation(v); err != nil {
        panic(err)
    }

    // 注册接口
    r.POST("/api/v1/register", func(c *gin.Context) {
        // 使用泛型函数
        req, err := validator.BindAndValidate[RegisterRequest](c, v)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "code":    400,
                "message": "Validation failed",
                "error":   err.Error(),
            })
            return
        }

        // 处理注册逻辑...
        c.JSON(http.StatusOK, gin.H{
            "code":    200,
            "message": "Registration successful",
            "data": gin.H{
                "username": req.Username,
                "email":    req.Email,
            },
        })
    })

    // 完善资料接口
    r.POST("/api/v1/profile/complete", func(c *gin.Context) {
        var req CompleteProfileRequest
        if err := v.Validate(c, &req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "code":    400,
                "message": "Invalid request",
                "error":   err.Error(),
            })
            return
        }

        // 处理业务逻辑...
        c.JSON(http.StatusOK, gin.H{
            "code":    200,
            "message": "Profile completed successfully",
        })
    })

    // 获取密码要求
    r.GET("/api/v1/password-requirements", func(c *gin.Context) {
        requirements := validator.GetPasswordRequirements()
        c.JSON(http.StatusOK, gin.H{
            "code":    200,
            "message": "Success",
            "data": gin.H{
                "requirements": requirements,
            },
        })
    })

    r.Run(":8080")
}
测试 API
# 成功注册
curl -X POST http://localhost:8080/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john123",
    "email": "john@example.com",
    "password": "SecureP@ssw0rd123",
    "age": 25
  }'

# 密码不符合要求
curl -X POST http://localhost:8080/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john123",
    "email": "john@example.com",
    "password": "weak",
    "age": 25
  }'

# 响应示例:
# {
#   "code": 400,
#   "message": "Validation failed",
#   "error": "password must contain: at least 12 characters, uppercase letter, lowercase letter, number and special character"
# }

最佳实践

1. 全局初始化验证器
package main

import "github.com/lite-lake/litecore-go/util/validator"

var GlobalValidator *validator.DefaultValidator

func init() {
    GlobalValidator = validator.NewDefaultValidator()

    // 注册所有自定义验证器
    GlobalValidator.RegisterValidation("username", validateUsername)
    validator.RegisterPasswordValidation(GlobalValidator)
    // ...
}
2. 分离请求结构体
// requests/user.go
package requests

type RegisterRequest struct {
    Username string `json:"username" validate:"required,alphanum,min=4,max=20"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,complexPassword"`
}

type LoginRequest struct {
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required"`
}
3. 统一错误处理
func HandleValidationError(c *gin.Context, err error) {
    if ve, ok := err.(*validator.ValidationError); ok {
        c.JSON(400, gin.H{
            "code":    400001,
            "message": "Validation failed",
            "details": ve.Message,
        })
        return
    }

    c.JSON(400, gin.H{
        "code":    400000,
        "message": "Invalid request",
        "error":   err.Error(),
    })
}
4. 提供友好的错误提示
// 在 API 文档中返回验证规则
c.JSON(200, gin.H{
    "validation_rules": gin.H{
        "username": "必填,4-20位字母数字",
        "email": "必填,有效的邮箱地址",
        "password": validator.GetPasswordRequirements(),
    },
})

常见问题

Q: 如何忽略某些字段的验证?

使用 omitempty 标签:

type UpdateUserRequest struct {
    Name  string `json:"name,omitempty" validate:"omitempty,max=50"`
    Email string `json:"email,omitempty" validate:"omitempty,email"`
}
Q: 如何验证多个条件?

使用逗号分隔多个标签:

Password string `json:"password" validate:"required,min=12,max=100,complexPassword"`
Q: 如何自定义错误消息?

可以在应用层进行转换:

func formatError(err error) map[string]string {
    messages := map[string]string{
        "required": "%s 不能为空",
        "email":    "%s 必须是有效的邮箱地址",
        "min":      "%s 长度不能少于 %s 个字符",
    }

    // 自定义格式化逻辑...
    return formattedMessages
}
Q: 验证器是线程安全的吗?

是的,DefaultValidator 创建后可以在多个 goroutine 中安全使用。

依赖

License

MIT License

Documentation

Overview

Package validator 基于 gin 框架的数据验证器,使用 go-playground/validator 提供结构体验证

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BindAndValidate

func BindAndValidate[T any](ctx *gin.Context, validator Validator) (*T, error)

BindAndValidate 绑定并验证请求(泛型辅助函数)

func GetPasswordRequirements

func GetPasswordRequirements() string

GetPasswordRequirements 获取密码要求说明(用于错误提示) 使用默认配置

func GetPasswordRequirementsWithConfig

func GetPasswordRequirementsWithConfig(config *PasswordConfig) string

GetPasswordRequirementsWithConfig 使用自定义配置获取密码要求说明

func RegisterPasswordValidation

func RegisterPasswordValidation(v *DefaultValidator) error

RegisterPasswordValidation 注册密码复杂度验证器到 validator 实例 使用默认配置

func RegisterPasswordValidationWithConfig

func RegisterPasswordValidationWithConfig(v *DefaultValidator, config *PasswordConfig) error

RegisterPasswordValidationWithConfig 使用自定义配置注册密码验证器

func ValidateComplexPassword

func ValidateComplexPassword(fl validator.FieldLevel) bool

ValidateComplexPassword 使用默认配置验证密码复杂度 这是一个便捷函数,使用默认配置进行验证

func ValidatePassword

func ValidatePassword(password string, config *PasswordConfig) error

ValidatePassword 使用自定义配置验证密码并返回详细错误信息 这个函数可以在服务层使用,提供更友好的错误提示

Types

type DefaultValidator

type DefaultValidator struct {
	// contains filtered or unexported fields
}

DefaultValidator 默认验证器(基于 go-playground/validator)

func NewDefaultValidator

func NewDefaultValidator() *DefaultValidator

NewDefaultValidator 创建默认验证器

func (*DefaultValidator) RegisterValidation

func (v *DefaultValidator) RegisterValidation(tag string, fn validator.Func) error

RegisterValidation 注册自定义验证函数 tag: 验证标签名(如 "complexPassword") fn: 验证函数,签名为 func(fl validator.FieldLevel) bool

func (*DefaultValidator) Validate

func (v *DefaultValidator) Validate(ctx *gin.Context, obj interface{}) error

Validate 验证请求

type PasswordConfig

type PasswordConfig struct {
	// MinLength 密码最小长度(默认 12)
	MinLength int
	// MaxLength 密码最大长度(默认 128)
	MaxLength int
	// RequireUpper 是否要求大写字母(默认 true)
	RequireUpper bool
	// RequireLower 是否要求小写字母(默认 true)
	RequireLower bool
	// RequireNumber 是否要求数字(默认 true)
	RequireNumber bool
	// RequireSpecial 是否要求特殊字符(默认 true)
	RequireSpecial bool
}

PasswordConfig 密码验证配置

func DefaultPasswordConfig

func DefaultPasswordConfig() *PasswordConfig

DefaultPasswordConfig 返回默认密码配置

type ValidationError

type ValidationError struct {
	Message string
	Errors  validator.ValidationErrors
}

ValidationError 验证错误

func (*ValidationError) Error

func (ve *ValidationError) Error() string

type Validator

type Validator interface {
	Validate(ctx *gin.Context, obj interface{}) error
}

Validator 验证器接口

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL