validator

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jan 14, 2025 License: MIT Imports: 8 Imported by: 0

README

Contributions Welcome Total Views Release

Validator Package

The validator package provides an extensible, customizable validation system built on top of validator/v10. It includes support for custom validation rules and error translations, making it ideal for applications that require flexible and descriptive error handling.

Features

  • Easy integration with go-playground/validator.
  • Support for custom validators with custom error messages.
  • Simplified API for struct validation.
  • Extensible design for adding more custom validators.

Usage

Basic Validation
package main

import (
    "fmt"
    "github.com/kittipat1413/go-common/framework/validator"
)

type User struct {
    Name  string `validate:"required"`
    Email string `validate:"required,email"`
    Age   int    `validate:"gte=0,lte=130"`
}

func main() {
    v, err := validator.NewValidator()
    if err != nil {
        fmt.Println("Error initializing validator:", err)
        return
    }

    user := User{
        Name:  "Alice",
        Email: "alice@example.com",
        Age:   30,
    }

    err = v.ValidateStruct(user)
    if err != nil {
        fmt.Println("Validation failed:", err)
    } else {
        fmt.Println("Validation passed")
    }
}

Custom Validators

You can create application-specific validation logic beyond the built-in rules provided by validator/v10. This package provides an interface called CustomValidator, which allows you to define custom validation rules, tags, and error message translations.

To create a custom validator, you must implement the CustomValidator interface, which consists of three methods:

type CustomValidator interface {
	// Tag returns the tag identifier used in struct field validation tags (e.g., `validate:"mytag"`).
	Tag() string
	// Func returns the validator.Func that performs the validation logic.
	Func() validator.Func
	// Translation returns the translation text and a custom translation function for the custom validator.
	// If you wish to use the default translation, return an empty string and nil.
	Translation() (translation string, customFunc validator.TranslationFunc)
}

Each method in the CustomValidator interface has a specific purpose:

  1. Tag(): Defines the unique identifier for your custom validation rule. This tag is used in struct field tags to apply the validator. For example, if Tag() returns "mytag", you can use it in a struct as validate:"mytag".
  2. Func(): Specifies the validation logic. This method returns a function that implements the validator.Func type, which is called by the validator during the validation process. The function receives a FieldLevel instance representing the field to validate. You should return true if the field passes validation, and false otherwise.
  3. Translation(): Defines the error message and translation function for this validator. If you want to use a custom error message format, return it in translation (using {0} for the field name and {1}, etc., for any parameters). The customFunc parameter allows additional customization for translating the error message, providing control over how the message displays.
    • If you return "" (empty string) and nil for Translation(), the default error message will be used.
Example: Creating a Custom Validator

The following example demonstrates how to create a custom validator that always fails and provides a custom error message:

package main

import (
    "fmt"
    ut "github.com/go-playground/universal-translator"
    validatorV10 "github.com/go-playground/validator/v10"
    "github.com/kittipat1413/go-common/framework/validator"
)

type MyValidator struct{}

// Tag returns the tag identifier used in struct field validation tags.
func (*MyValidator) Tag() string {
    return "mytag"
}

// Func returns the validator.Func that performs the validation logic.
func (*MyValidator) Func() validatorV10.Func {
    return func(fl validatorV10.FieldLevel) bool {
        // Custom validation logic (always fails for demonstration)
        return false
    }
}

// Translation returns the translation text and an custom translation function for the custom validator.
func (*MyValidator) Translation() (string, validatorV10.TranslationFunc) {
    translationText := "{0} failed custom validation"
    customTransFunc := func(ut ut.Translator, fe validatorV10.FieldError) string {
        // {0} will be replaced with fe.Field()
        t, _ := ut.T(fe.Tag(), fe.Field())
        return t
    }
    return translationText, customTransFunc
}

Explanation

  • Tag: The Tag() method returns "mytag", which you can use in struct tags like validate:"mytag".
  • Func: The Func() method returns a function that always fails for demonstration purposes.
  • Translation: The Translation() method returns a custom error message and translation function The error message is "{0} failed custom validation", where {0} will be replaced with the field name (fe.Field()).
Using Custom Validators

To register a custom validator, you need to pass an instance of the custom validator to the NewValidator function using the WithCustomValidator option. The following example demonstrates how to use the custom validator created in the previous section:

type Data struct {
    Field1 string `validate:"mytag"`
    Field2 string `validate:"required"`
    Field3 int    `validate:"gte=0,lte=130"`
}

func main() {
    v, err := validator.NewValidator(
        validator.WithCustomValidator(new(MyValidator)),
        // Add more custom validators here
    )
    if err != nil {
        fmt.Println("Error initializing validator:", err)
        return
    }

    data := Data{
        Field1: "test", // Will fail due to custom validation logic in MyValidator
        Field3: 200,    // Out of range (greater than 130)
    }

    err = v.ValidateStruct(data)
    if err != nil {
        fmt.Println("Validation failed:", err)
    } else {
        fmt.Println("Validation passed")
    }
}
  • Expected Output:
    Validation failed: Field1 failed custom validation, Field2 is a required field, Field3 must be 130 or less
    

Note: The package is built to make adding your own validators straightforward. If you need a domain-specific custom validator, you can implement the CustomValidator interface and use it directly in your codebase, without waiting for a merge into go-common. If you feel your custom validator could benefit others, consider sharing it here via a pull request or issue.

Using Custom Field Names in Validation Errors

To make validation errors more readable, especially in APIs that use JSON serialization, you can customize field names in error messages to match the json struct tags. The package provides a convenient option for this with WithTagNameFunc.

  • JSON Tag Name Function: The package includes a predefined JSONTagNameFunc to automatically use JSON field names in validation error messages.
Registering JSONTagNameFunc

To register JSONTagNameFunc when creating a Validator instance, use the WithTagNameFunc option:

v, err := validator.NewValidator(
	validator.WithTagNameFunc(validator.JSONTagNameFunc),
)

With this setup, any validation error messages will use the names specified in the json tags. For example:

type User struct {
	FullName string `json:"full_name" validate:"required"`
	Email    string `json:"email" validate:"required,email"`
	Age      int    `json:"age" validate:"gte=0,lte=130"`
}

user := User{
	Email: "invalid-email",
	Age:   150,
}

err := v.ValidateStruct(user)
if err != nil {
	fmt.Println("Validation failed:", err)
}
  • Expected Output:
    Validation failed: full_name is a required field, email must be a valid email address, age must be 130 or less
    

Examples

Documentation

Index

Constants

This section is empty.

Variables

View Source
var JSONTagNameFunc = func(fld reflect.StructField) string {
	name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
	if name == "-" {
		return ""
	}
	return name
}

JSONTagNameFunc extracts the field name from the `json` struct tag.

Functions

This section is empty.

Types

type CustomValidator

type CustomValidator interface {
	// Tag returns the tag identifier used in struct field validation tags (e.g., `validate:"tag"`).
	Tag() string
	// Func returns the validator.Func that performs the validation logic.
	Func() validator.Func
	// Translation returns the translation text and an custom translation function for the custom validator.
	// To use the default translation, return an empty string and nil.
	Translation() (translation string, customFunc validator.TranslationFunc)
}

CustomValidator defines the interface that custom validators must implement. It requires methods to return the validation tag, function, and translation details.

type Validator

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

Validator holds the validator and translator instances used for struct validation.

func NewValidator

func NewValidator(opts ...ValidatorOption) (*Validator, error)

NewValidator creates and returns a new Validator instance with the provided options. It initializes the validator, applies custom options, sets up the translator, and registers default translations.

Example:

v, err := NewValidator(
    WithCustomValidator(DateValidator{}),
)
if err != nil {
    // handle error
}

func (*Validator) Struct

func (v *Validator) Struct(s interface{}) error

Struct validates the provided struct using the validator instance. This method is introduced for compatibility with validator v10, which expects a method named Struct to perform validation on structs.

It uses the same underlying validation logic as ValidateStruct, translating validation error messages using the provided translator instance.

Example:

err := v.Struct(myStruct)
if err != nil {
    // Handle validation errors
}

func (*Validator) ValidateStruct

func (v *Validator) ValidateStruct(s interface{}) error

ValidateStruct validates the provided struct using the validator instance. It returns an error containing all validation errors with messages translated using the translator.

Example:

err := v.ValidateStruct(myStruct)
if err != nil {
    // Handle validation errors
}

type ValidatorOption

type ValidatorOption func(*validator.Validate, ut.Translator) error

ValidatorOption defines a functional option for configuring the validator instance.

func WithCustomValidator

func WithCustomValidator(cv CustomValidator) ValidatorOption

WithCustomValidator registers a custom validator along with its translation. It uses the CustomValidator interface to get the tag, function, and translation details.

func WithTagNameFunc

func WithTagNameFunc(tagNameFunc func(fld reflect.StructField) string) ValidatorOption

WithTagNameFunc registers a custom function to derive field names in validation errors. For example, you can use this to specify that validation errors should display `json` tag names.

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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