validator

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Dec 11, 2021 License: MIT Imports: 14 Imported by: 0

README

GoDoc

This is a validation package for golang that returns human readable error messages. Its ideal for validating input data for public facing restful api's.

It has the following features

  • Combination of validators with logical operators (e.g. &, |, ())
  • Cross field and cross struct validation (e.g. firstName and lastName must be set)
  • Custom validators, e.g. validator.AddRule("name", func(ps..) error)
  • Customizable i18n aware error messages using the golang.org/x/text/message package

How it works

Validator uses struct tags to verify data passed in to apis. Use the validate tag to apply various Rules that the field must follow (e.g. validate:"email"). You can add custom validation rules as necessary by implementing your own validator.Rule functions. This package also comes with several common rules referenced below such as number:min,max, email, password, etc.

Example

package main

import "github.com/marksalpeter/validator"

type User struct {
	FirstName    string `json:"firstName,omitempty" validate:"name"`           // FirstName cannot contain numbers or special characters
	LastName     string `json:"lastName,omitempty" validate:"name"`            // LastName cannot contain numbers or special characters
	EmailAddress string `json:"emailAddress,omitempty" validate:"email"`       // EmailAddress must be a valid email address
	PhoneNumber  string `json:"phoneNumber,omitempty" validate:"number:11,15"` // PhoneNumber must be 11-15 numbers e.g 15551234567
}

func main() {

	// displays all validation errors
	var user User
	v := validator.New()
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'firstName' must be a valid name","'lastName' must be a valid name","'emailAddress' must be a valid email address","'phoneNumber' must contain only numbers"]
	}

	// set the properties
	user.FirstName = "First"
	user.LastName = "Last"
	user.EmailAddress = "email@address.com"
	user.PhoneNumber = "15551234567"
	if err := v.Validate(); err != nil {
		panic(err)
	}
	fmt.Println("user data is valid!")
}

Advanced Example

package main

import "github.com/marksalpeter/validator"

// User can either set name or firstName and lastName
type User struct {
	Name      string `json:"name,omitempty" validate:"name | or:FirstName,LastName"`
	FirstName string `json:"firstName,omitempty" validate:"empty | (name & and:LastName)"`
	LastName  string `json:"lastName,omitempty" validate:"empty | (name & and:FirstName)"`
}

func main() {

	var user User
	v := validator.New()

	// empty struct fails on `or:FirstName,LastName`
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["either 'name', 'firstName' and/or 'lastName' must be set"]
	}

	// only first name fails on `(name & and:LastName)`
	user.FirstName = "First"
	user.LastName = ""
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'firstName' and 'lastName' must be set"]
	}

	// only last name fails on (name & and:FirstName)
	user.FirstName = ""
	user.LastName = "Last"
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'lastName' and 'firstName' must be set"]
	}

	// first and last name are set first name passes
	user.FirstName = "First"
	user.LastName = "Last"
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // never reached
	}

	// name passes
	user.Name = "First Last"
	user.FirstName = ""
	user.LastName = ""
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // never reached
	}

}

Validation Rules

This package comes with several default validation rules:

Rule Description
required required returns an error if the filed contains the zero value of the type or nil
empty empty returns an error if the field is not empty
name name returns an error if the field doesn't contain a valid name
email email returns an error if the field doesn't contain a valid email address
password password returns an error if the field doesn't contain a valid password
number number retuns an error if the field doesn't contain numbers only
letters letters retuns an error if the field doesn't contain letters only
eq eq returns an error if the field does not == one of the params passed in
xor xor returns an error when more than one or zero of either the field that it is applied to or any of the field names passed as params are set to a non zero value
or or returns an error when neither the field that it is applied to nor any of the field names passed as params are set to a non zero value
and and returns an error when the field that it is applied to or any of the field names passed as params are set to the zero value

Required ^

Required returns an error if the filed contains the zero value of the type or nil.

Example
type Struct struct {
	Field  string `json:"field" validate:"required"` // 'field' is required
}

Empty ^

Empty returns an error if the field is not empty. It should be 'or'd together with other rules that require manditory input

Example
type Struct struct {
	Field  string `json:"field" validate:"empty | email"` // 'field' must be a valid email address or not set at all
}

Name ^

Name returns an error if the field doesn't contain a valid name i.e. no numbers or most special characters, excepting characters that may be in a name like a - and allowing foreign language letters with accent marks as well as spaces This prevents things like emails or phone numbers from being entered as a name.

Example
type Struct struct {
	Field  string `json:"field" validate:"name"` // 'field' must be a valid name
}

Email ^

Email returns an error if the field doesn't contain a valid email address

Example
type Struct struct {
	Field  string `json:"field" validate:"email"` // 'field' must be a valid email address
}

Password ^

Password returns an error if the field doesn't contain a valid password

Example
type Struct struct {
	Field  string `json:"field" validate:"password"` // 'field' must be a valid password
}

Number ^

Number retuns an error if the field doesn't contain numbers only

Example
type Struct struct {
	Field   string `json:"field" validate:"number"`      // 'field' must contain only numbers
	Field2  string `json:"field2" validate:"number:3,5"` // 'field2' must be 3 to 5 digits
	Field3  uint   `json:"field3" validate:"number:3,5"` // 'field3' must be 3 to 5
}

Letters ^

Letters retuns an error if the field doesn't contain letters only

Example
type Struct struct {
	Field  string `json:"field" validate:"letters"` // 'field' can only take letters and spaces
}

EQ ^

EQ returns an error if the field does not == one of the params passed in

Example
type Struct struct {
	Field  string `json:"field" validate:"eq:one,two,three"` // 'field' must equal either "one", "two", or "three"
}

XOR ^

XOR returns an error when more than one or zero of either the field that it is applied to or any of the field names passed as params are set to a non zero value

Example
type Struct struct {
	Field  string `json:"field" validate:" xor:Field2"` // either "field" or "Field2" must be set
	Field2 string
}

OR ^

OR returns an error when neither the field that it is applied to nor any of the field names passed as params are set to a non zero value

Example
type Struct struct {
	Field  string `json:"field" validate:"or:Field2"` // either "field" or "Field2" or both must be set
	Field2 string
}

AND ^

AND returns an error when the field that it is applied to or any of the field names passed as params are set to the zero value

Example
type Struct struct {
	Field  string `json:"field" validate:"and:Field2"` // "field" and "Field2" must be set
	Field2 string
}

Special Mentions

This package was heavily inspired by the go-playground validator and was originally envisioned as a more flexible, powerful version of the same basic concept.

How to Contribute

Open a pull request 😁

License

Distributed under MIT License, please see license file within the code for more details.

Documentation

Overview

Package validator validates the fields of structs by applying the rules embeded in a fields "validate" tag to the value of that field. It is designed to return plain english error messages that refer to field names by their json key. These validation error messages are intended to be presented to the outside world.

Rule Syntax

Rules can be joined together with "and"s (&) and "or"s (|)

type Struct struct {
  Field   string `json:"field" validate:"omitempty | email"`   // 'field' must be a valid email address or not set at all
  Field2  string `json:"field2" validate:"required & letters"` // 'field' is required and must be comprised of only letters and spaces
}

Comma seperated params can also be passed to a rule, but not every rule has parameters. Check the godoc of the spefic rule you're applying for an example of how to use it.

type Struct struct {
  Field  string `json:"field" validate:"eq:one,two,three"` // 'field' must equal either "one", "two", or "three"
}

Finally, its worth noting the validators can cross reference other fields.

type Struct struct {
  Field  string `json:"field" validate:" xor:Field2"` // either "field" or "field2" must be set
  Field2 string `json:"field2"`
}

Index

Examples

Constants

View Source
const DefaultTag = "validate"

DefaultTag is the tage used if Config.Tag is not set

Variables

View Source
var DefaultRules = Rules{
	"required": Required,
	"empty":    Empty,
	"name":     Name,
	"email":    Email,
	"password": Password,
	"number":   Number,
	"letters":  Letters,
	"eq":       EQ,
	"xor":      XOR,
	"or":       OR,
	"and":      AND,
}

DefaultRules is the default set of rules the validator will be created with

View Source
var DefaultValidator = New()

DefaultValidator is the default validator used by the `Validate` and `CheckSyntax` funcs

Functions

func AND

func AND(ps *RuleParams) error

AND returns an error when the field that it is applied to or any of the field names passed as params are set to the zero value

Example

type Struct struct {
  Field  string `json:"field" validate:"and:Field2"` // "field" and "Field2" must be set
  Field2 string
}

func AddRule

func AddRule(name string, rule func(*RuleParams) error)

AddRule adds a rule to the `DefaultRules`

func CheckSyntax

func CheckSyntax(i interface{}) error

CheckSyntax cycles though all of the validation tags and returns bad syntax errors instead of panicing

func EQ

func EQ(ps *RuleParams) error

EQ returns an error if the field does not == one of the params passed in

Example

type Struct struct {
  Field  string `json:"field" validate:"eq:one,two,three"` // 'field' must equal either "one", "two", or "three"
}

func Email

func Email(ps *RuleParams) error

Email returns an error if the field doesn't contain a valid email address

Example

type Struct struct {
  Field  string `json:"field" validate:"email"` // 'field' must be a valid email address
}

func Empty

func Empty(ps *RuleParams) error

Empty returns an error if the field is not empty. It should be 'or'd together with other rules that require manditory input

Example

type Struct struct {
  Field  string `json:"field" validate:"empty | email"` // 'field' must be a valid email address or not set at all
}

func Letters

func Letters(ps *RuleParams) error

Letters retuns an error if the field doesn't contain letters only

Example

type Struct struct {
  Field  string `json:"field" validate:"letters"` // 'field' can only take letters and spaces
}

func Name

func Name(ps *RuleParams) error

Name returns an error if the field doesn't contain a valid name I.e. no numbers or most special characters, excepting characters that may be in a name like a - and allowing foreign language letters with accent marks as well as spaces This prevents things like emails or phone numbers from being entered as a name.

Example

type Struct struct {
  Field  string `json:"field" validate:"name"` // 'field' must be a valid name
}

func Number

func Number(ps *RuleParams) error

Number retuns an error if the field doesn't contain numbers only

Example

type Struct struct {
  Field   string `json:"field" validate:"number"`      // 'field' must contain only numbers
  Field2  string `json:"field2" validate:"number:3,5"` // 'field2' must be 3 to 5 digits
  Field3  uint   `json:"field3" validate:"number:3,5"` // 'field3' must be 3 to 5
}

func OR

func OR(ps *RuleParams) error

OR returns an error when neither the field that it is applied to nor any of the field names passed as params are set to a non zero value

Example

type Struct struct {
  Field  string `json:"field" validate:"or:Field2"` // either "field" or "Field2" or both must be set
  Field2 string
}

func Password

func Password(ps *RuleParams) error

Password returns an error if the field doesn't contain a valid password Example

type Struct struct {
  Field  string `json:"field" validate:"password"` // 'field' must be a valid password
}

func Required

func Required(ps *RuleParams) error

Required returns an error if the filed contains the zero value of the type or nil.

Example

type Struct struct {
  Field  string `json:"field" validate:"required"` // 'field' is required
}

func Validate

func Validate(i interface{}, tags ...language.Tag) error

Validate validates a struct or a slice based on the information passed to the 'validate' tag. based on the 'DefaultRules' The error returned will be in English by default, but thay can be changed to any supported language by passing in the cooresponding language tag

func XOR

func XOR(ps *RuleParams) error

XOR returns an error when more than one or zero of either the field that it is applied to or any of the field names passed as params are set to a non zero value

Example

type Struct struct {
  Field  string `json:"field" validate:" xor:Field2"` // either "field" or "Field2" must be set
  Field2 string
}
Example
package main

import (
	"fmt"

	"github.com/marksalpeter/validator"
)

func main() {
	// User can either set name or firstName and lastName
	type User struct {
		Name      string `json:"name,omitempty" validate:"name | or:FirstName,LastName"`
		FirstName string `json:"firstName,omitempty" validate:"empty | (name & and:LastName)"`
		LastName  string `json:"lastName,omitempty" validate:"empty | (name & and:FirstName)"`
	}

	var user User
	v := validator.New()

	// empty struct fails on `or:FirstName,LastName`
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["either 'name', 'firstName' and/or 'lastName' must be set"]
	}

	// only first name fails on `(name & and:LastName)`
	user.FirstName = "First"
	user.LastName = ""
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'firstName' and 'lastName' must be set"]
	}

	// only last name fails on (name & and:FirstName)
	user.FirstName = ""
	user.LastName = "Last"
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'lastName' and 'firstName' must be set"]
	}

	// first and last name are set first name passes
	user.FirstName = "First"
	user.LastName = "Last"
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // never reached
	}

	// name passes
	user.Name = "First Last"
	user.FirstName = ""
	user.LastName = ""
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // never reached
	}

}
Output:

["either 'name', 'firstName' and/or 'lastName' must be set"]
["'firstName' and 'lastName' must be set"]
["'lastName' and 'firstName' must be set"]

Types

type Config

type Config struct {
	Tag   string
	Rules Rules
}

Config configures the validator

type Errors

type Errors interface {
	Errors() []error
}

Errors contains a slice of errors

type FieldError

type FieldError struct {
	Path    string `json:"path,omitempty"`
	Message error  `json:"message,omitempty"`
}

FieldError is the error returned when a field rule returns an error

func (*FieldError) As added in v0.1.2

func (fe *FieldError) As(i interface{}) bool

Is implements errors.As

func (*FieldError) Error

func (fe *FieldError) Error() string

Error implements errors.Error

func (*FieldError) Is added in v0.1.2

func (fe *FieldError) Is(err error) bool

Is implements errors.Is

func (*FieldError) MarshalJSON

func (fe *FieldError) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface

type FieldErrors

type FieldErrors []error

FieldErrors are slice of FieldError generate by the rules

func (*FieldErrors) Add

func (es *FieldErrors) Add(errs ...error)

Add merges FieldErrors together

func (FieldErrors) As added in v0.1.1

func (es FieldErrors) As(err interface{}) bool

As implements errors.As

func (FieldErrors) Error

func (es FieldErrors) Error() string

Error implements errors.Error

func (FieldErrors) Errors

func (es FieldErrors) Errors() []error

Errors implements Errors

func (FieldErrors) Is added in v0.1.1

func (es FieldErrors) Is(err error) bool

Is implements errors.Is

type Rule

type Rule func(*RuleParams) error

Rule is a rule that is applied to a field in a struct

type RuleParams

type RuleParams struct {
	// Tag represents the language the error message should be in
	Tag language.Tag

	// FieldName is the name of the field the rule is validating
	// TODO: add example
	FieldName string

	// Params are arguments that were passed to the rule
	// TODO: add example
	Params []string

	// Root is the interface{} that was passed to the Validator.Validate method
	Root reflect.Value

	// Parent is the struct{} that the Field belongs to. This can be the same as Root if a simple struct was passed in to the Validator.Validate func
	Parent reflect.Value

	// Field is the field on the struct whose value is being validated
	Field reflect.Value
}

RuleParams is the set of parameters a rule processes to determine if there was a validation error

type Rules

type Rules map[string]Rule

Rules are a set of rules that the `Validator` will look up by name in order to appy them to fields in a struct

func (Rules) Add

func (rs Rules) Add(name string, rule func(*RuleParams) error)

Add adds a rule to the map of rules

type Validator

type Validator interface {
	// CheckSyntax cycles though all of the validation tags and returns bad syntax errors instead of panicing
	CheckSyntax(interface{}) error

	// Validate validates a struct or a slice based on the information passed to the 'validate' tag.
	// The error returned will be in English by default, but they can be changed to Spanish by setting the optional language.Tag.
	Validate(interface{}, ...language.Tag) error
}

Validator validates structs and slices

Example
package main

import (
	"fmt"

	"github.com/marksalpeter/validator"
)

func main() {
	type User struct {
		FirstName    string `json:"firstName,omitempty" validate:"name"`           // FirstName cannot contain numbers or special characters
		LastName     string `json:"lastName,omitempty" validate:"name"`            // LastName cannot contain numbers or special characters
		EmailAddress string `json:"emailAddress,omitempty" validate:"email"`       // EmailAddress must be a valid email address
		PhoneNumber  string `json:"phoneNumber,omitempty" validate:"number:11,15"` // PhoneNumber must be 11-15 numbers e.g 15551234567
	}

	// displays all validation errors
	var user User
	v := validator.New()
	if err := v.Validate(&user); err != nil {
		fmt.Println(err) // prints ["'firstName' must be a valid name","'lastName' must be a valid name","'emailAddress' must be a valid email address","'phoneNumber' must contain only numbers"]
	}

	// set the properties
	user.FirstName = "First"
	user.LastName = "Last"
	user.EmailAddress = "email@address.com"
	user.PhoneNumber = "15551234567"
	if err := v.Validate(&user); err != nil {
		panic(err)
	}
	fmt.Println("user data is valid!")

}
Output:

["'firstName' must be a valid name","'lastName' must be a valid name","'emailAddress' must be a valid email address","'phoneNumber' must contain only numbers"]
user data is valid!

func New

func New(cfg ...*Config) Validator

New returns a new Validator It will parse the validation tags in the following fashion:

Example

  type Example struct {
	  Field string `validator:"one | (two & three)"`
  }
  New().Validate(&Example{})

The field will be deemed valid if

one(Example.Field) == nil || (two(Example.Field) == nil && three(Example.Field) == nil)

Jump to

Keyboard shortcuts

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