validate

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2021 License: MIT Imports: 6 Imported by: 0

README

go-validate

Yet another Go struct/object validation package, with a focus on simplicity, flexibility, and full control over validation logic.

Go Reference GitHub tag (latest SemVer) Actions Status Coverage GitHub issues GitHub pull requests License Status

Add validation to any type, by simply implementing the Validatable interface:

type Validatable interface {
	Validate() error
}

Import

import "github.com/romdo/go-validate"

Example

type Order struct {
	Books []*Book `json:"books"`
}

type Book struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

func (s *Book) Validate() error {
	var errs error
	if s.Title == "" {
		errs = validate.Append(errs, &validate.Error{
			Field: "Title", Msg: "is required",
		})
	}

	// Helper to perform the same kind of check as above for Title.
	errs = validate.Append(errs, validate.RequireField("Author", s.Author))

	return errs
}

func main() {
	errs := validate.Validate(&Order{Books: []*Book{{Title: ""}}})

	for _, err := range validate.Errors(errs) {
		fmt.Println(err.Error())
	}
}

Above example produces the following output:

books.0.title: is required
books.0.author: is required

Documentation

Please see the Go Reference for documentation and examples.

LICENSE

MIT

Documentation

Overview

Package validate is yet another Go struct/object validation package, with a focus on simplicity, flexibility, and full control over validation logic.

Interface

To add validation to any type, simply implement the Validatable interface:

type Validatable interface {
    Validate() error
}

To mark a object as failing validation, the Validate method simply needs to return a error.

When validating array, slice, map, and struct types each item and/or field that implements Validatable will be validated, meaning deeply nested structs can be fully validated, and the nested path to each object is tracked and reported back any validation errors.

Multiple Errors

Multiple errors can be reported from the Validate method using one of the available Append helper functions which append errors together. Under the hood the go.uber.org/multierr package is used to represent multiple errors as a single error return type, and you can in fact just directly use multierr in the a type's Validate method.

Structs and Field-specific Errors

When validating a struct, you are likely to have multiple errors for multiple fields. To specify which field on the struct the error relates to, you have to return a *validate.Error instead of a normal Go error type. For example:

type Book struct {
    Title  string
    Author string
}

func (s *Book) Validate() error {
    var errs error

    if s.Title == "" {
        errs = validate.Append(errs, &validate.Error{
            Field: "Title", Msg: "is required",
        })
    }

    if s.Author == "" {
        // Yields the same result as the Title field check above.
        errs = validate.AppendFieldError(errs, "Author", "is required")
    }

    return errs
}

With the above example, if you validate a empty *Book:

err := validate.Validate(&Book{})
for _, e := range validate.Errors(err) {
    fmt.Println(e.Error())
}

The following errors would be printed:

Title: is required
Kind: is required

Error type

All errors will be wrapped in a *Error before being returned, which is used to keep track of the path and field the error relates to. There are various helpers available to create Error instances.

Handling Validation Errors

As mentioned above, multiple errors are wrapped up into a single error return value using go.uber.org/multierr. You can access all errors individually with Errors(), which accepts a single error, and returns []error. The Errors() function is just wrapper around multierr.Errors(), so you could use that instead if you prefer.

Struct Field Tags

Fields on a struct which customize the name via a json, yaml, or form field tag, will automatically have the field name converted to the name in the tag in returned *Error types with a non-empty Field value.

You can customize the field name conversion logic by creating a custom Validator instance, and calling FieldNameFunc() on it.

Nested Validatable Objects

All items/fields on any structs, maps, slices or arrays which are encountered will be validated if they implement the Validatable interface. While traversing nested data structures, a path list tracks the location of the current object being validation in relation to the top-level object being validated. This path is used within the field in the final output errors.

By default path components are joined with a dot, but this can be customized when using a custom Validator instance and calling FieldJoinFunc() passing in a custom function to handle path joining.

As an example, if our Book struct from above is nested within the following structs:

type Order struct {
    Items []*Item `json:"items"`
}

type Item struct {
    Book *Book `json:"book"`
}

And we have a Order where the book in the second Item has a empty Author field:

err := validate.Validate(&Order{
    Items: []*Item{
        {Book: &Book{Title: "The Traveler", Author: "John Twelve Hawks"}},
        {Book: &Book{Title: "The Firm"}},
    },
})
for _, e := range validate.Errors(err) {
    fmt.Println(e.Error())
}

Then we would get the following error:

items.1.book.Author: is required

Note how both "items" and "book" are lower cased thanks to the json tags on the struct fields, while our Book struct does not have a json tag for the Author field.

Also note that the error message does not start with "Order". The field path is relative to the object being validated, hence the top-level object is not part of the returned field path.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Append

func Append(errs error, err error) error

Append combines two errors together into a single new error which internally keeps track of multiple errors via go.uber.org/multierr. If either error is a previously combined multierr, the returned error will be a flattened list of all errors.

func AppendError

func AppendError(errs error, msg string) error

AppendError appends a new *Error type to errs with the Msg field populated with the provided msg.

func AppendFieldError

func AppendFieldError(errs error, field, msg string) error

AppendFieldError appends a new *Error type to errs with Field and Msg populated with given field and msg values.

func DefaultFieldJoin

func DefaultFieldJoin(path []string, field string) string

DefaultFieldJoin is the default FieldJoinFunc used by Validator.

func DefaultFieldName

func DefaultFieldName(fld reflect.StructField) string

DefaultFieldName is the default FieldNameFunc used by Validator.

Uses json, yaml, and form field tags to lookup field name first.

func Errors

func Errors(err error) []error

Errors returns a slice of all errors appended into the given error.

func RequireField

func RequireField(field string, value interface{}) error

RequireField returns a Error type for the given field if provided value is empty/zero.

func Validate

func Validate(v interface{}) error

Validate will validate the given object. Structs, maps, slices, and arrays will have each of their fields/items validated, effectively performing a deep-validation.

Types

type Error

type Error struct {
	Field string
	Msg   string
	Err   error
}

Error represents validation errors, and implements Go's error type. Field indicates the struct field the validation error is relevant to, which is the full nested path relative to the top-level object being validated.

func (*Error) Error

func (s *Error) Error() string

func (*Error) Is

func (s *Error) Is(target error) bool

func (*Error) Unwrap

func (s *Error) Unwrap() error

type FieldJoinFunc

type FieldJoinFunc func(path []string, field string) string

FieldJoinFunc joins a path slice with a given field. Both path and field may be empty values.

type FieldNameFunc

type FieldNameFunc func(reflect.StructField) string

FieldNameFunc is a function which converts a given reflect.StructField to a string. The default will lookup json, yaml, and form field tags.

type Validatable

type Validatable interface {
	Validate() error
}

Validatable is the primary interface that a object needs to implement to be validatable with Validator.

Validation errors are reported by returning a error from the Validate method. Multiple errors can be combined into a single error to return with Append() and related functions, or via go.uber.org/multierr.

For validatable structs, the field the validation error relates to can be specified by returning a *Error type with the Field value specified.

type Validator

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

Validator validates Validatable objects.

func New

func New() *Validator

New creates a new Validator.

func (*Validator) FieldJoinFunc

func (s *Validator) FieldJoinFunc(f FieldJoinFunc)

FieldJoinFunc allows setting a custom FieldJoinFunc method. It receives a string slice of parent fields, and a string of the field name the error is reported against. All parent paths, must be joined with the current.

func (*Validator) FieldNameFunc

func (s *Validator) FieldNameFunc(f FieldNameFunc)

FieldNameFunc allows setting a custom FieldNameFunc method. It receives a reflect.StructField, and must return a string for the name of that field. If the returned string is empty, validation will not run against the field's value, or any nested data within.

func (*Validator) Validate

func (s *Validator) Validate(data interface{}) error

Validate will validate the given object. Structs, maps, slices, and arrays will have each of their fields/items validated, effectively performing a deep-validation.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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