validator

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2021 License: MIT Imports: 10 Imported by: 16

README

Validator Go Reference Go Report Card CI Status

This is a simple input validator for go programs which can be used to test inputs at the boundary of your application.

It uses a fluent API to allow chaining of validators to keep things concise and terse.

There is also a provided Validator interface which can applied to structs and tested in the like of http handlers or error handlers.

Error structure

Validation functions are ran per field and multiple functions can be evaluated per field.

Any validation errors found are then stored in a map[string][]string. This can be printed using the .String() method or can be encoded to Json and wrapped in an errors object for example.

For an idea of the json output see this below example validation error:

{
    "errors": {
        "count": [
            "value 0 should be greater than 0"
        ],
        "isEnabled": [
            "value true does not evaluate to false"
        ]
    }
}

In this example I wanted my errors to be wrapped in an errors object, you may just want them output raw and unwrapped or wrapped in something else.

This is up to you to decide how best to handle the presentation of the error list.

Usage

There are two main ways of using the library, either via inline checks or by implementing the validator.Validator interface.

To get started though, call validator.New().

From this you can then chain validators, the idea is you supply the field name and then a series of one or more validator functions.

Inline Chaining

Below is an inline method shown, this shows the fluent nature of the API allowing chaining of validators.

Each Validate call is for a single field but multiple validator functions can be added per field as can be seen for the "dob" field.

    func(s *svc) Create(ctx context.Context, req Request) error{
        if err := validator.New().
            Validate("name", validator.Length(req.Name, 4, 10)).
            Validate("dob", validator.NotEmpty(req.DOB), validator.DateBefore(req.DOB, time.Now().AddDate(-16, 0, 0))).
            Validate("isEnabled", validator.Bool(req.IsEnabled, false)).
            Validate("count", validator.PositiveInt(req.Count)).Err(); err != nil {
                return err
        }
    }

Note - the final call here is the .Err() method, this will return nil if no errors are found or error if one or more have been found.

Struct Validation

The second method to validate is by implementing the validator.Validator interface on a struct.

The interface is very simple:

    type Validator interface {
        Validate() ErrValidation
    }

Taking an example from the examples directory, you can apply to a struct as shown:

    type Request struct {
        Name      string    `json:"name"`
        DOB       time.Time `json:"dob"`
        IsEnabled bool      `json:"isEnabled"`
        Count     int       `json:"count"`
    }
    
    // Validate implements validator.Validator and evaluates Request.
    func (r *Request) Validate() validator.ErrValidation {
        return validator.New().
            Validate("name", validator.Length(r.Name, 4, 10)).
            Validate("dob", validator.NotEmpty(r.DOB), validator.DateBefore(r.DOB, time.Now().AddDate(-16, 0, 0))).
            Validate("isEnabled", validator.Bool(r.IsEnabled, false)).
            Validate("count", validator.PositiveInt(r.Count))
    }

This is an ideal usecase for handling common errors in a global error handler, you can simply parse your requests, check, if they implement the interface and evaluate the struct. An Example of this is found in the examples.

Examples

There are examples in the examples directory, you can clone the repo and have a play with these to ensure the validator meets your needs.

Functions

All functions are currently located in the functions file.

These must return a validator.ValidationFunc function and can be wrapped to allow custom params to be passed.

Here is an example from the functions.go file:

    func Length(val string, min, max int) ValidationFunc {
        return func() error {
            if len(val) >= min && len(val) <= max {
                return nil
            }
            return fmt.Errorf(validateLength, val, min, max)
        }
    }

Pretty simple! You can add your own compatible functions in your code base and call them in the Validate(..,...) methods.

You can also apply one time functions to the Validate calls as shown:

    Validate("name", validator.Length(name, 1, 20), func() error{
        if mything == 0{
            return fmt.Errorf("oh no")
        }
        return nil
    })

Contributing

I've so far added a limited set of validation functions, if you have an idea for some useful functions feel free to open an issue and PR.

Documentation

Overview

Package validator allows simple boundary checking of user input either against a struct or by checking values using the provided fluent API.

It stores the output of any errors found in a usable structure that lists the field names and all validation errors associated with it.

This allows the output to be serialised and returned in 400 responses or equivalent with readable and useful messages.

It is designed to be simple to use and comes with a series of built in validation functions. You can add your own simply by wrapping the provided ValidationFunc.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewFromError added in v0.1.3

func NewFromError(fieldName string, err error) error

NewFromError is a simple way of creating a one off error rather than calling a validate function.

  test, err := thing()
  if err != nil{
	    // this error is froma. bad request, convert to a validation error
	    return validator.NewFromError("myField", err)
  }

This will then be classed as a validation error and handled how you desire.

func NewSingleError added in v0.1.3

func NewSingleError(fieldName string, msg []string) error

NewSingleError is a simple way of creating a one off error rather than calling a validate function.

  test, err := thing()
  if err != nil{
	    // this error is froma. bad request, convert to a validation error
	    return validator.NewSingleError("myField", []string{"this went wrong"})
  }

This will then be classed as a validation error and handled how you desire.

Types

type ErrValidation

type ErrValidation map[string][]string

ErrValidation contains a list of field names and a list of errors found against each. This can then be converted for output to a user.

func New

func New() ErrValidation

New will create and return a new ErrValidation which can have Validate functions chained.

func (ErrValidation) BadRequest

func (e ErrValidation) BadRequest() bool

BadRequest implements the err BadRequest behaviour from the https://github.com/theflyingcodr/lathos package.

func (ErrValidation) Err

func (e ErrValidation) Err() error

Err will return nil if no errors are found, ie all validators return valid or ErrValidation if an error has been found.

func (ErrValidation) Error

func (e ErrValidation) Error() string

Error implements the Error interface and ensure that ErrValidation can be passed as an error as well and being printable.

func (ErrValidation) String

func (e ErrValidation) String() string

String implements the Stringer interface and will return a string based representation of any errors found.

func (ErrValidation) Validate

func (e ErrValidation) Validate(field string, fns ...ValidationFunc) ErrValidation

Validate will log any errors found when evaluating the list of validation functions supplied to it.

type ValidationFunc

type ValidationFunc func() error

ValidationFunc defines a simple function that can be wrapped and supplied with arguments.

Typical usage is shown:

 func Length(val string, min, max int) ValidationFunc {
	    return func() error {
		    if len(val) >= min && len(val) <= max {
			    return nil
		    }
		    return fmt.Errorf(validateLength, val, min, max)
	    }
  }

func AnyString added in v0.1.3

func AnyString(val string, vv ...string) ValidationFunc

AnyString will check if the provided string is in a set of allowed values.

func BetweenInt

func BetweenInt(val, min, max int) ValidationFunc

BetweenInt will ensure an int, val, is at least min and at most max.

func BetweenInt64

func BetweenInt64(val, min, max int64) ValidationFunc

BetweenInt64 will ensure an int64, val, is at least min and at most max.

func BetweenUInt64

func BetweenUInt64(val, min, max uint64) ValidationFunc

BetweenUInt64 will ensure an int64, val, is at least min and at most max.

func Bool

func Bool(val, exp bool) ValidationFunc

Bool is a simple check to ensure that val matches either true / false as defined by exp.

func DateAfter

func DateAfter(val, exp time.Time) ValidationFunc

DateAfter will ensure that a date/time, val, occurs after exp.

func DateBefore

func DateBefore(val, exp time.Time) ValidationFunc

DateBefore will ensure that a date/time, val, occurs before exp.

func DateEqual

func DateEqual(val, exp time.Time) ValidationFunc

DateEqual will ensure that a date/time, val, matches exactly exp.

func Email added in v0.1.3

func Email(val string) ValidationFunc

Email will check that a string is a valid email address.

func Empty added in v0.1.3

func Empty(v interface{}) ValidationFunc

Empty will ensure that a value, val, is empty. rules are: int: == 0 string: == "" or whitespace slice: is nil or len == 0 map: is nil and len == 0

func HasPrefix

func HasPrefix(val, prefix string) ValidationFunc

HasPrefix ensures string, val, has a prefix matching prefix.

func IsHex

func IsHex(val string) ValidationFunc

IsHex will check that a string, val, is valid Hexadecimal.

func IsNumeric

func IsNumeric(val string) ValidationFunc

IsNumeric will pass if a string, val, is an Int.

func MatchBytes

func MatchBytes(val []byte, r *regexp.Regexp) ValidationFunc

MatchBytes will check that a byte array, val, matches the provided regular expression.

func MatchString

func MatchString(val string, r *regexp.Regexp) ValidationFunc

MatchString will check that a string, val, matches the provided regular expression.

func MaxInt

func MaxInt(val, max int) ValidationFunc

MaxInt will ensure an Int, val, is at most Max in value.

func MaxInt64

func MaxInt64(val, max int64) ValidationFunc

MaxInt64 will ensure an Int64, val, is at most Max in value.

func MaxUInt64

func MaxUInt64(val, max uint64) ValidationFunc

MaxUInt64 will ensure an Int64, val, is at most Max in value.

func MinInt

func MinInt(val, min int) ValidationFunc

MinInt will ensure an Int, val, is at least min in value.

func MinInt64

func MinInt64(val, min int64) ValidationFunc

MinInt64 will ensure an Int64, val, is at least min in value.

func MinUInt64

func MinUInt64(val, min uint64) ValidationFunc

MinUInt64 will ensure an uint64, val, is at least min in value.

func NoPrefix

func NoPrefix(val, prefix string) ValidationFunc

NoPrefix ensures a string, val, does not have the supplied prefix.

func NotEmpty

func NotEmpty(v interface{}) ValidationFunc

NotEmpty will ensure that a value, val, is not empty. rules are: int: > 0 string: != "" or whitespace slice: not nil and len > 0 map: not nil and len > 0

func PositiveInt

func PositiveInt(val int) ValidationFunc

PositiveInt will ensure an int, val, is > 0.

func PositiveInt64

func PositiveInt64(val int64) ValidationFunc

PositiveInt64 will ensure an int64, val, is > 0.

func PositiveUInt64

func PositiveUInt64(val uint64) ValidationFunc

PositiveUInt64 will ensure an uint64, val, is > 0.

func StrLength added in v0.0.2

func StrLength(val string, min, max int) ValidationFunc

StrLength will ensure a string, val, has a length that is at least min and at most max.

func StrLengthExact added in v0.0.2

func StrLengthExact(val string, length int) ValidationFunc

StrLengthExact will ensure a string, val, is exactly length.

func UKPostCode

func UKPostCode(val string) ValidationFunc

UKPostCode will validate that a string, val, is a valid UK PostCode. It does not check the postcode exists, just that it matches an agreed pattern.

func USZipCode

func USZipCode(val string) ValidationFunc

USZipCode will validate that a string, val, matches a US USZipCode pattern. It does not check the zipcode exists, just that it matches an agreed pattern.

func (ValidationFunc) String

func (v ValidationFunc) String() string

String satisfies the String interface and returns the underlying error string that is returned by evaluating the function.

type Validator

type Validator interface {
	Validate() ErrValidation
}

Validator is an interface that can be implemented on a struct to run validation checks against its properties.

This is designed to be used in http handlers or middleware where all requests can be checked for this behaviour and evaluated.

type MyStruct struct{
  MyProp string
  ShouldBeTrue bool
}

func(m *MyStruct) Validate() error{
    return validator.New().
        Validate("myProp", validator.Length(m.MyProp,10,20)).
        Validate("shouldBeTrue", validator.Bool(m.ShouldBeTrue, true))
}

Directories

Path Synopsis
examples
cli
http-inline
A simple stdlib http handler that validates a request inline - rather than using the validator.Validator interface.
A simple stdlib http handler that validates a request inline - rather than using the validator.Validator interface.
http-validate
This has the same logic as the http-inline example but it has implemented validator.Validator and as such requests can be checked in a single place.
This has the same logic as the http-inline example but it has implemented validator.Validator and as such requests can be checked in a single place.

Jump to

Keyboard shortcuts

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