Slim

v - Data Validation Library

v is a powerful and flexible data validation library, providing a chainable API, rich built-in validation rules, custom error messages, and internationalization support. Supports validation for form data, structs, API parameters, and various other scenarios. Minimum supported Go version: 1.21.0.

Installation

go get -u go-slim.dev/v

Quick Start

package mainimport (    "fmt"    "go-slim.dev/v")func main() {    // Simple validation    err := v.Validate(        v.Value("", "username", "Username").Required(),        v.Value("invalid-email", "email", "Email").IsEmail(),    )    if err != nil {        fmt.Println(err)        // Output: Username is required        //         Email is not a valid email address    }}

Core Concepts

Validator

A validator is any type that implements the Validatable interface:

type Validatable interface {    Validate() error}

Value Validator (Valuer)

Valuer is the core value validator for validating a single value:

// Value(value, field name, label)validator := v.Value("alice@example.com", "email", "Email Address")
  • Value: The actual data to validate
  • Field Name: Field identifier (e.g., “email”)
  • Label: User-readable field name (e.g., “Email Address”)

Basic Usage

1. Single Value Validation

import "go-slim.dev/v"// Required validationerr := v.Value("", "username", "Username").    Required().    Validate()// Error: Username is required// Email validationerr := v.Value("alice@example.com", "email", "Email").    Required().    IsEmail().    Validate()// Pass// Chained validationerr := v.Value("pass123", "password", "Password").    Required().    MinLength(8).    MaxLength(32).    Validate()// Error: Password minimum length is 8

2. Multiple Value Validation

Validate - Collect All Errors

Executes all validators and collects all errors:

err := v.Validate(    v.Value("", "username", "Username").Required(),    v.Value("invalid", "email", "Email").IsEmail(),    v.Value("123", "age", "Age").Between(18, 100),)if err != nil {    // err contains all validation errors    errs := err.(*v.Errors)    // Get first error    first := errs.First()    fmt.Println(first.String())    // Get errors for specific field    emailErrs := errs.Get("email")    // Convert to map    errMap := errs.ToMap()    // map[string][]*v.Error{    //     "username": [...],    //     "email": [...],    // }}

Check - Fail Fast

Returns immediately on first error:

err := v.Check(    v.Value("", "username", "Username").Required(),    v.Value("invalid", "email", "Email").IsEmail(),  // Won't execute)// Only returns username error

3. Map Data Validation

Validate map data from forms or JSON:

data := map[string]any{    "username": "alice",    "email":    "alice@example.com",    "age":      25,}field := v.Map(data)err := v.Validate(    field("username", "Username").Required().MinLength(3),    field("email", "Email").Required().IsEmail(),    field("age", "Age").Required().Between(18, 100),)

4. Struct Validation

Implement the Validatable interface:

type User struct {    Username string    Email    string    Age      int}func (u *User) Validate() error {    return v.Validate(        v.Value(u.Username, "username", "Username").            Required().            MinLength(3).            MaxLength(20),        v.Value(u.Email, "email", "Email").            Required().            IsEmail(),        v.Value(u.Age, "age", "Age").            Required().            Between(18, 100),    )}// Usageuser := &User{Username: "alice", Email: "invalid", Age: 15}if err := user.Validate(); err != nil {    // Handle validation errors}

Validation Rules

Required Validation

// Required - Value must be non-emptyv.Value("", "name", "Name").Required()// RequiredIf - Conditional requiredisAdmin := truev.Value("", "role", "Role").RequiredIf(isAdmin)// RequiredWith - Depends on other valuesphone := ""email := "alice@example.com"v.Value(phone, "phone", "Phone").RequiredWith([]any{email})// If email is not empty, phone is required

String Validation

val := v.Value("hello", "text", "Text")// Length validationval.Length(5)              // Length equals 5val.MinLength(3)           // Minimum length 3val.MaxLength(10)          // Maximum length 10val.LengthBetween(3, 10)   // Length between 3-10// Content validationval.Contains("ell")        // Contains substringval.ContainsAny("aeiou")   // Contains any characterval.ContainsRune('h')      // Contains specific characterval.Excludes("xyz")        // Doesn't contain substringval.ExcludesAll("123")     // Doesn't contain any specified charactersval.ExcludesRune('x')      // Doesn't contain specific characterval.StartsWith("he")       // Starts with...val.StartsNotWith("x")     // Doesn't start with...val.EndsWith("lo")         // Ends with...val.EndsNotWith("x")       // Doesn't end with...// Character typesval.IsAlpha()              // Only contains lettersval.IsAlphanumeric()       // Only contains letters and numbersval.IsAlphaUnicode()       // Only contains letters and Unicode charactersval.IsAlphanumericUnicode() // Only contains letters, numbers, and Unicodeval.IsNumeric()            // Numeric stringval.IsAscii()              // ASCII charactersval.IsLower()              // Lowercaseval.IsUpper()              // Uppercase// Format validationval.IsHexadecimal()        // Hexadecimalval.IsBase64()             // Base64 encodingval.IsBase64URL()          // Base64 URL encodingval.IsURLEncoded()         // URL encodingval.IsHTML()               // HTML contentval.IsHTMLEncoded()        // HTML escaping

Numeric Validation

val := v.Value(42, "age", "Age")// Comparisonval.Equal(42)              // Equalsval.NotEqual(0)            // Not equalsval.GreaterThan(18)        // Greater thanval.GreaterEqualThan(18)   // Greater or equalval.LessThan(100)          // Less thanval.LessEqualThan(100)     // Less or equalval.Between(18, 100)       // In rangeval.NotBetween(0, 17)      // Not in range// Type validationval.IsNumber()             // Number typeval.IsNumeric()            // Numericval.IsBool()               // Boolean

Format Validation

// Network-relatedv.Value("alice@example.com", "email", "Email").IsEmail()v.Value("https://example.com", "url", "URL").IsURL()v.Value("192.168.1.1", "ip", "IP Address").IsIP()v.Value("192.168.1.1", "ip", "IP Address").IsIPv4()v.Value("::1", "ip", "IP Address").IsIPv6()v.Value("00:11:22:33:44:55", "mac", "MAC Address").IsMAC()// Phone numberv.Value("+8613800138000", "phone", "Phone").IsE164()v.Value("13800138000", "phone", "Phone").IsPhoneNumber()// UUIDv.Value("550e8400-e29b-41d4-a716-446655440000", "id", "ID").IsUUID()v.Value("...", "id", "ID").IsUUID3()v.Value("...", "id", "ID").IsUUID4()v.Value("...", "id", "ID").IsUUID5()v.Value("01ARZ3NDEKTSV4RRFFQ69G5FAV", "id", "ID").IsULID()// JWTv.Value("eyJhbGc...", "token", "Token").IsJwt()// Hashv.Value("5d41402abc4b2a76b9719d911017c592", "hash", "Hash").IsMD5()v.Value("...", "hash", "Hash").IsSHA256()v.Value("...", "hash", "Hash").IsSHA384()v.Value("...", "hash", "Hash").IsSHA512()// Colorv.Value("#FF5733", "color", "Color").IsHexColor()v.Value("rgb(255, 87, 51)", "color", "Color").IsRgb()v.Value("rgba(255, 87, 51, 0.5)", "color", "Color").IsRgba()v.Value("hsl(9, 100%, 60%)", "color", "Color").IsHsl()v.Value("hsla(9, 100%, 60%, 0.5)", "color", "Color").IsHsla()v.Value("#FF5733", "color", "Color").IsColor()  // Any color format// Coordinatesv.Value(39.9042, "lat", "Latitude").IsLatitude()v.Value(116.4074, "lng", "Longitude").IsLongitude()// Timev.Value("2024-01-15", "date", "Date").IsDatetime("2006-01-02")v.Value("Asia/Shanghai", "tz", "Timezone").IsTimezone()// Otherv.Value("1.2.3", "version", "Version").IsSemver()v.Value(`{"key":"value"}`, "data", "Data").IsJson()v.Value("/path/to/file", "path", "Path").IsFile()v.Value("/path/to/dir", "path", "Path").IsDir()

Enum Validation

// OneOf - Value must be in liststatus := "pending"v.Value(status, "status", "Status").    OneOf([]any{"pending", "active", "inactive"})// Practical applicationrole := "admin"v.Value(role, "role", "Role").    OneOf([]any{"admin", "user", "guest"})

Collection Validation

Every - All Elements Validation

All elements must pass validation:

numbers := []int{1, 2, 3, 4, 5}err := v.Value(numbers, "numbers", "Number List").    Every(func(item *v.Item) any {        // item.Index: element index        // item.Value: element value        return v.Value(item.Value, "number", "Number").            Between(1, 10)    }).    Validate()// Map validationdata := map[string]int{    "a": 1,    "b": 2,    "c": 3,}err := v.Value(data, "data", "Data").    Every(func(item *v.Item) any {        // item.Key: map key        // item.Value: map value        return v.Value(item.Value, "value", "Value").            GreaterThan(0)    }).    Validate()

Some - Any Element Validation

At least one element must pass validation:

emails := []string{"invalid", "alice@example.com", "bad"}err := v.Value(emails, "emails", "Email List").    Some(func(item *v.Item) any {        return v.Value(item.Value, "email", "Email").            IsEmail()    }).    Validate()// Pass (at least one valid email)

Conditional Validation

When - Conditional Execution

Only execute validation when condition is met:

isAdmin := trueerr := v.Value("moderator", "role", "Role").    When(isAdmin, func(val *v.Valuer) {        val.OneOf([]any{"admin", "superadmin"})    }).    Validate()// Only validate role when isAdmin is true

Match - Pattern Matching

Execute different validations based on value:

userType := "email"identifier := "alice@example.com"err := v.Value(identifier, "identifier", "Identifier").    Match(func(m *v.Matcher) {        m.Branch("email", func(val *v.Valuer) error {            return val.IsEmail().Validate()        })        m.Branch("phone", func(val *v.Valuer) error {            return val.IsPhoneNumber().Validate()        })        m.Fallback(func(val *v.Valuer) error {            return v.NewError("invalid_type")        })    }).    Validate()// Practical: validate based on login typeloginType := "phone"loginValue := "13800138000"err := v.Value(loginValue, "login", "Login Credential").    Match(func(m *v.Matcher) {        // Login with email        m.Branch("email", func(val *v.Valuer) error {            return val.Required().IsEmail().Validate()        })        // Login with phone        m.Branch("phone", func(val *v.Valuer) error {            return val.Required().IsPhoneNumber().Validate()        })        // Login with username        m.Branch("username", func(val *v.Valuer) error {            return val.Required().MinLength(3).Validate()        })        // Default case        m.Fallback(func(val *v.Valuer) error {            return v.NewError("unsupported_login_type")        })    }).    Validate()

Custom Validation

Custom - Custom Rule

// Return boolv.Value("hello", "text", "Text").    Custom("custom_check", func(val any) any {        str := val.(string)        return len(str) > 3  // true/false    })// Return errorv.Value("hello", "text", "Text").    Custom("custom_check", func(val any) any {        str := val.(string)        if len(str) < 3 {            return errors.New("Too short")        }        return nil  // or true    })// Return Validatablev.Value(user, "user", "User").    Custom("check_user", func(val any) any {        u := val.(*User)        return u  // Calls u.Validate()    })// Practical: check if username already existsv.Value("alice", "username", "Username").    Custom("username_exists", func(val any) any {        exists := db.UserExists(val.(string))        if exists {            return errors.New("Username already exists")        }        return true    }, v.ErrorFormat("Username {value} is already taken"))

Type Validation

import "reflect"// Typeof - Validate reflection typev.Value("hello", "text", "Text").    Typeof(reflect.String)v.Value(123, "num", "Number").    Typeof(reflect.Int)// IsString - Convenient string type validationv.Value("hello", "text", "Text").IsString()

Advanced Features

1. Composite Validators

Every - All Validators Must Pass

validator := v.Every(    v.Value("alice", "username", "Username").Required(),    v.Value("alice@example.com", "email", "Email").IsEmail(),)err := validator.Validate()// All validations must pass

Some - Any Validator Passes

// User can provide either email or phonevalidator := v.Some(    v.Value(email, "email", "Email").IsEmail(),    v.Value(phone, "phone", "Phone").IsPhoneNumber(),)err := validator.Validate()// At least one must pass

2. IndexBy - Grouped Validation

For multiple parameter groups, only one complete group is required:

var index int// User can provide: email+password OR phone+codeerr := v.IndexBy(&index, [][]any{    {email, password},      // Group 0    {phone, code},          // Group 1}).Validate()if err == nil {    switch index {    case 0:        // Login with email and password    case 1:        // Login with phone and code    }}// Practical applicationtype LoginRequest struct {    Email    string    Password string    Phone    string    Code     string}func (r *LoginRequest) Validate() error {    var index int    err := v.Validate(        v.IndexBy(&index, [][]any{            {r.Email, r.Password},            {r.Phone, r.Code},        }),    )    if err != nil {        return err    }    // Execute different validation based on index    switch index {    case 0:        return v.Validate(            v.Value(r.Email, "email", "Email").Required().IsEmail(),            v.Value(r.Password, "password", "Password").Required().MinLength(6),        )    case 1:        return v.Validate(            v.Value(r.Phone, "phone", "Phone").Required().IsPhoneNumber(),            v.Value(r.Code, "code", "Code").Required().Length(6),        )    }    return nil}

3. Functional Validation

Wrap - Wrap Validation Function

// Create reusable validatorcheckUsername := v.Wrap("alice", func(val any) error {    username := val.(string)    if db.UserExists(username) {        return errors.New("Username already exists")    }    return nil})err := checkUsername.Validate()

Checker - Function Type

// Checker implements Validatable interfacevar passwordCheck v.Checker = func() error {    if password != confirmPassword {        return errors.New("Passwords do not match")    }    return nil}err := v.Validate(    v.Value(password, "password", "Password").Required(),    passwordCheck,  // Can use directly)

Error Handling

Error Structure

type Error struct {    // Error code (e.g., "required", "is_email")    Code() string    // Error formatting template    Format() string    // Formatting parameters    Params() map[string]any    // Field name    Field() string    // Field label (user-readable)    Label() string    // Validated value    Value() any    // Internal error    Internal() error}

Custom Error Messages

// Using ErrorFormatv.Value("", "username", "Username").    Required(v.ErrorFormat("Please enter {label}"))// Using ErrorParam to add parametersv.Value("ab", "username", "Username").    MinLength(3,        v.ErrorFormat("{label} requires at least {min} characters, currently only {length}"),        v.ErrorParam("length", 2),    )// Using ErrorCode to customize error codev.Value("", "username", "Username").    Required(v.ErrorCode("custom_required"))

Errors Collection

err := v.Validate(    v.Value("", "username", "Username").Required(),    v.Value("invalid", "email", "Email").IsEmail(),)if err != nil {    errs := err.(*v.Errors)    // Check if empty    if !errs.IsEmpty() {        // Get first error        first := errs.First()        fmt.Println(first.String())        // Get all errors        all := errs.All()        // Get errors for specific field        usernameErrs := errs.Get("username")        // Convert to map[field name][]*Error        errMap := errs.ToMap()        for field, fieldErrs := range errMap {            fmt.Printf("%s: %d errors\n", field, len(fieldErrs))        }        // String representation (all errors)        fmt.Println(errs.String())    }}

Internationalization

Setting Default Translator

import "go-slim.dev/v"// Custom translation functionv.SetDefaultTranslator(func(message string, params map[string]any) string {    // Load translation from file or database    translated := i18n.Translate(message, params)    // Replace parameter placeholders    for key, value := range params {        translated = strings.ReplaceAll(            translated,            "{"+key+"}",            fmt.Sprintf("%v", value),        )    }    return translated})

Built-in Chinese Messages

The v package includes built-in Chinese error messages:

// These error codes have Chinese translations"required"           -> "{label} is required""is_email"           -> "{label} is not a valid email address""is_phone_number"    -> "{label} is not a valid phone number""min_length"         -> "{label} minimum length is {min}""between"            -> "{label} must be greater than or equal to {min} and less than or equal to {max}"// ... etc.

Use Cases

1. Web Form Validation

import (    "go-slim.dev/slim"    "go-slim.dev/v")type RegisterForm struct {    Username        string `json:"username"`    Email           string `json:"email"`    Password        string `json:"password"`    ConfirmPassword string `json:"confirm_password"`    Age             int    `json:"age"`    Terms           bool   `json:"terms"`}func (f *RegisterForm) Validate() error {    return v.Validate(        v.Value(f.Username, "username", "Username").            Required().            MinLength(3).            MaxLength(20).            IsAlphanumeric(),        v.Value(f.Email, "email", "Email").            Required().            IsEmail(),        v.Value(f.Password, "password", "Password").            Required().            MinLength(8).            MaxLength(32),        v.Value(f.ConfirmPassword, "confirm_password", "Confirm Password").            Required().            Equal(f.Password,                v.ErrorFormat("Passwords do not match"),            ),        v.Value(f.Age, "age", "Age").            Required().            Between(18, 100),        v.Value(f.Terms, "terms", "Terms of Service").            Custom("must_accept", func(val any) any {                return val.(bool) == true            }, v.ErrorFormat("Must accept terms of service")),    )}func registerHandler(c slim.Context) error {    var form RegisterForm    if err := c.Bind(&form); err != nil {        return c.JSON(400, map[string]any{"error": "Invalid request data"})    }    if err := form.Validate(); err != nil {        errs := err.(*v.Errors)        return c.JSON(422, map[string]any{            "errors": errs.ToMap(),        })    }    // Create user...    return c.JSON(200, map[string]any{"success": true})}

2. API Parameter Validation

func updateUserHandler(c slim.Context) error {    data := map[string]any{        "name":  c.FormValue("name"),        "email": c.FormValue("email"),        "age":   c.FormValue("age"),    }    field := v.Map(data)    err := v.Validate(        field("name", "Name").Required().MinLength(2),        field("email", "Email").Required().IsEmail(),        field("age", "Age").When(data["age"] != "", func(val *v.Valuer) {            val.Between(18, 100)        }),    )    if err != nil {        return c.JSON(422, map[string]any{"errors": err})    }    // Update user...    return c.JSON(200, map[string]any{"success": true})}

3. Complex Business Validation

type Order struct {    UserID      int    Items       []OrderItem    PaymentType string    CardNumber  string    AlipayID    string    TotalAmount float64}type OrderItem struct {    ProductID int    Quantity  int    Price     float64}func (o *Order) Validate() error {    return v.Validate(        // User ID must exist        v.Value(o.UserID, "user_id", "User ID").            Required().            Custom("user_exists", func(val any) any {                return db.UserExists(val.(int))            }),        // Order items validation        v.Value(o.Items, "items", "Order Items").            Required().            NotEmpty().            Every(func(item *v.Item) any {                orderItem := item.Value.(OrderItem)                return v.Validate(                    v.Value(orderItem.ProductID, "product_id", "Product ID").                        Required().                        Custom("product_exists", func(val any) any {                            return db.ProductExists(val.(int))                        }),                    v.Value(orderItem.Quantity, "quantity", "Quantity").                        Required().                        Between(1, 100),                    v.Value(orderItem.Price, "price", "Price").                        Required().                        GreaterThan(0),                )            }),        // Payment method validation        v.Value(o.PaymentType, "payment_type", "Payment Method").            Required().            OneOf([]any{"card", "alipay", "wechat"}).            Match(func(m *v.Matcher) {                // Credit card payment requires card number                m.Branch("card", func(val *v.Valuer) error {                    return v.Value(o.CardNumber, "card_number", "Card Number").                        Required().                        Length(16).                        Validate()                })                // Alipay payment requires Alipay account                m.Branch("alipay", func(val *v.Valuer) error {                    return v.Value(o.AlipayID, "alipay_id", "Alipay Account").                        Required().                        Validate()                })            }),        // Total amount validation        v.Value(o.TotalAmount, "total_amount", "Total Amount").            Required().            Custom("amount_match", func(val any) any {                calculated := 0.0                for _, item := range o.Items {                    calculated += item.Price * float64(item.Quantity)                }                // Allow 0.01 error margin                diff := calculated - o.TotalAmount                return diff < 0.01 && diff > -0.01            }, v.ErrorFormat("Total amount doesn't match order items")),    )}

4. Multi-Scenario Validation

type User struct {    ID       int    Username string    Email    string    Password string    Role     string}// Validation for creationfunc (u *User) ValidateCreate() error {    return v.Validate(        v.Value(u.Username, "username", "Username").            Required().            MinLength(3).            Custom("username_unique", func(val any) any {                return !db.UsernameExists(val.(string))            }),        v.Value(u.Email, "email", "Email").            Required().            IsEmail().            Custom("email_unique", func(val any) any {                return !db.EmailExists(val.(string))            }),        v.Value(u.Password, "password", "Password").            Required().            MinLength(8),        v.Value(u.Role, "role", "Role").            Required().            OneOf([]any{"user", "admin"}),    )}// Validation for updatefunc (u *User) ValidateUpdate() error {    return v.Validate(        v.Value(u.ID, "id", "User ID").            Required().            Custom("user_exists", func(val any) any {                return db.UserExists(val.(int))            }),        v.Value(u.Username, "username", "Username").            When(u.Username != "", func(val *v.Valuer) {                val.MinLength(3)            }),        v.Value(u.Email, "email", "Email").            When(u.Email != "", func(val *v.Valuer) {                val.IsEmail()            }),        // Password not required for update        v.Value(u.Password, "password", "Password").            When(u.Password != "", func(val *v.Valuer) {                val.MinLength(8)            }),    )}

5. Nested Struct Validation

type Address struct {    Street  string    City    string    ZipCode string}func (a *Address) Validate() error {    return v.Validate(        v.Value(a.Street, "street", "Street").Required(),        v.Value(a.City, "city", "City").Required(),        v.Value(a.ZipCode, "zip_code", "Zip Code").            Required().            Length(6).            IsNumeric(),    )}type Profile struct {    Name    string    Age     int    Address Address}func (p *Profile) Validate() error {    return v.Validate(        v.Value(p.Name, "name", "Name").Required(),        v.Value(p.Age, "age", "Age").Between(18, 100),        // Nested validation        &p.Address,  // Implements Validatable interface    )}

API Reference

Core Functions

// Validation executionfunc Validate(validations ...Validatable) errorfunc Check(validations ...Validatable) error// Value validatorfunc Value(value any, field, label string) *Valuer// Map validatorfunc Map(data map[string]any) func(name, label string) *Valuer// Composite validationfunc Every(validators ...Validatable) Checkerfunc Some(validators ...Validatable) Checkerfunc IndexBy(index *int, values [][]any, options ...ErrorOption) Checker// Wrap functionfunc Wrap(value any, check func(any) error) Checker// Error creationfunc NewError(code string, options ...ErrorOption) *Error// Error optionsfunc ErrorFormat(format string) ErrorOptionfunc ErrorParam(key string, value any) ErrorOptionfunc ErrorCode(code string) ErrorOption// Internationalizationfunc SetDefaultTranslator(translator Translator)func DefaultTranslator() Translator

Valuer Methods

// Requiredfunc (v *Valuer) Required(options ...ErrorOption) *Valuerfunc (v *Valuer) RequiredIf(condition bool, options ...ErrorOption) *Valuerfunc (v *Valuer) RequiredWith(values []any, options ...ErrorOption) *Valuer// Stringfunc (v *Valuer) Length(n int, options ...ErrorOption) *Valuerfunc (v *Valuer) MinLength(min int, options ...ErrorOption) *Valuerfunc (v *Valuer) MaxLength(max int, options ...ErrorOption) *Valuerfunc (v *Valuer) LengthBetween(min, max int, options ...ErrorOption) *Valuerfunc (v *Valuer) Contains(substr string, options ...ErrorOption) *Valuerfunc (v *Valuer) StartsWith(prefix string, options ...ErrorOption) *Valuerfunc (v *Valuer) EndsWith(suffix string, options ...ErrorOption) *Valuer// Numericfunc (v *Valuer) Equal(another any, options ...ErrorOption) *Valuerfunc (v *Valuer) Between(min, max any, options ...ErrorOption) *Valuerfunc (v *Valuer) GreaterThan(min any, options ...ErrorOption) *Valuerfunc (v *Valuer) LessThan(max any, options ...ErrorOption) *Valuer// Formatfunc (v *Valuer) IsEmail(options ...ErrorOption) *Valuerfunc (v *Valuer) IsPhoneNumber(options ...ErrorOption) *Valuerfunc (v *Valuer) IsURL(options ...ErrorOption) *Valuerfunc (v *Valuer) IsIP(options ...ErrorOption) *Valuerfunc (v *Valuer) IsUUID(options ...ErrorOption) *Valuer// Collectionfunc (v *Valuer) Every(handle func(item *Item) any, options ...ErrorOption) *Valuerfunc (v *Valuer) Some(handle func(item *Item) any, options ...ErrorOption) *Valuer// Conditionalfunc (v *Valuer) When(condition bool, then func(*Valuer)) *Valuerfunc (v *Valuer) Match(handle func(m *Matcher)) *Valuer// Customfunc (v *Valuer) Custom(code string, check func(val any) any, options ...ErrorOption) *Valuer// Executefunc (v *Valuer) Validate() error

Notes

  1. Empty Value Handling:

    • Non-Required fields skip subsequent validation when empty
    • Use NotEmpty to validate non-empty but not required cases
  2. Error Collection:

    • Validate collects all errors
    • Check returns immediately on first error
    • Choose appropriate approach to balance performance and user experience
  3. Custom Validation Return Values:

    • true or nil: Validation passes
    • false: Validation fails, use default error message
    • error: Validation fails, use custom error message
    • Validatable: Calls its Validate() method
  4. Performance Considerations:

    • Custom validations (like database queries) may be slow
    • Consider caching validation results in business layer
    • Use Every carefully with large amounts of data
  5. Error Message Placeholders:

    • {label}: Field label
    • {value}: Field value
    • {field}: Field name
    • Custom parameters: Add via ErrorParam
  6. Chained Calls:

    • All validation methods return *Valuer, supporting chaining
    • Finally call Validate() to execute validation
    • Or pass as parameter to v.Validate()
  7. Type Safety:

    • Custom validations require manual type assertion
    • Mind handling panics from type assertions
    • Use Typeof to pre-validate types
Navigate Select esc Close search