ling-baseling-base

校验

Struct tag 驱动的数据校验与自定义规则

在线 Playground

在浏览器中直接体验本页相关 API,无需本地安装 Go 环境。

common/validate

Struct-tag-driven data validation with built-in rules, custom rule registration, nested struct validation, and slice validation.

Quick Start

import "github.com/LingByte/ling-base/common/validate"

type User struct {
    Name     string `validate:"required,min=3,max=50"`
    Email    string `validate:"required,email"`
    Age      int    `validate:"min=18,max=120"`
    Password string `validate:"required,min=8"`
    Confirm  string `validate:"eqfield=Password"`
}

user := User{Name: "ab", Email: "invalid-email", Age: 5}
err := validate.Validate(user)
// err is *validate.Errors with field-specific messages

if errs, ok := err.(validate.Errors); ok {
    for _, fe := range errs {
        fmt.Printf("%s: %s\n", fe.Field, fe.Message)
    }
}

Built-in Rules

RuleDescriptionExample
requiredMust not be zero valuevalidate:"required"
min=NMin value (numbers) or length (strings/slices)validate:"min=3"
max=NMax value or lengthvalidate:"max=50"
len=NExact length or valuevalidate:"len=10"
eq=NMust equal Nvalidate:"eq=42"
ne=NMust not equal Nvalidate:"ne=0"
gt=NGreater than Nvalidate:"gt=0"
gte=NGreater than or equalvalidate:"gte=18"
lt=NLess than Nvalidate:"lt=100"
lte=NLess than or equalvalidate:"lte=120"
oneof=a b cMust be one of listed valuesvalidate:"oneof=red green blue"
emailValid email addressvalidate:"email"
urlValid URLvalidate:"url"
ipValid IP addressvalidate:"ip"
ipv4Valid IPv4validate:"ipv4"
ipv6Valid IPv6validate:"ipv6"
alphaOnly alpha charactersvalidate:"alpha"
alphanumOnly alphanumericvalidate:"alphanum"
numericOnly numeric charactersvalidate:"numeric"
contains=sMust contain substringvalidate:"contains=@
startswith=sMust start with svalidate:"startswith=usr_"
endswith=sMust end with svalidate:"endswith=.com"
regex=patternMust match regexvalidate:"regex=^[A-Z]{2}$"
eqfield=NameMust equal another fieldvalidate:"eqfield=Password"
nefield=NameMust not equal another fieldvalidate:"nefield=Username"
gtfield=NameMust be greater than fieldvalidate:"gtfield=MinPrice"
gtefield=NameMust be ≥ fieldvalidate:"gtefield=StartDate"
ltefield=NameMust be ≤ fieldvalidate:"ltefield=EndDate"
uniqueSlice elements must be uniquevalidate:"unique"
diveValidate slice/map elementsvalidate:"dive"
nostructlevelSkip nested struct validationvalidate:"nostructlevel"

Custom Rules

import (
    "fmt"
    "regexp"
    "github.com/LingByte/ling-base/common/validate"
)

var phoneRegex = regexp.MustCompile(`^\d{11}$`)

validate.AddRule("phone", func(value any, param string, parent any) error {
    s, ok := value.(string)
    if !ok {
        return validate.ErrInvalidType
    }
    if !phoneRegex.MatchString(s) {
        return fmt.Errorf("invalid phone number")
    }
    return nil
})

type Contact struct {
    Phone string `validate:"required,phone"`
}

Nested Validation

Nested structs are validated automatically:

type Address struct {
    Street string `validate:"required"`
    City   string `validate:"required"`
}

type User struct {
    Name    string  `validate:"required"`
    Address Address `validate:"required"`
}

// If Address fields are empty, errors will include "Address.Street" etc.

Slice Validation with dive

type Team struct {
    Members []Member `validate:"required,dive"`
}

type Member struct {
    Name  string `validate:"required,min=2"`
    Email string `validate:"required,email"`
}

// Each element in Members is validated individually.
// Errors include "Members[0].Email", "Members[1].Name", etc.

Single Value Validation

err := validate.ValidateWithTag("test@example.com", "required,email")
// nil if valid

Error Handling

err := validate.Validate(user)
if err != nil {
    errs := err.(validate.Errors)
    if errs.Has("Email") {
        // handle email error
    }
    for _, fe := range errs {
        fmt.Printf("%s: %s (rule: %s)\n", fe.Field, fe.Message, fe.Rule)
    }
}

Slice / Map Validation (standalone)

Validate slices and maps outside of a struct context:

// Validate each element of a slice
errs := validate.ValidateSlice([]string{"a", "ab", "abc"}, "min=2")
// errs[0] = error for "a" (length < 2)
// errs is nil if all valid

// Validate each value in a map
errs := validate.ValidateMap(map[string]int{"a": 1, "b": 5}, "gt=3")
// errs["a"] = error for 1 (not > 3)

Rule Introspection

// Check if a rule exists
validate.HasRule("email")    // true
validate.HasRule("phone")    // false (unless custom-added)

// List all registered rules
rules := validate.RegisteredRules()

// Reset to default rules (removes custom rules)
validate.ResetRules()

License

MIT

On this page