validator

package module
v2.0.3 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2024 License: MIT Imports: 11 Imported by: 0

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.

Installation

GoValidator now utilises generics toi reduce the number of function duplication previously required. This requires go1.18 or above.

If you wish to have the version that doesn't require generics (this is no longer maintained), install as shown:

go get github.com/CGX-Ltd/govalidator@v0.1.3

To get the latest version with Generics, install using the below:

go get github.com/CGX-Ltd/govalidator/v2

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

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

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 Number

type Number interface {
	constraints.Integer | constraints.Float
}

Number defines all number types.

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 Any

func Any[T comparable](val T, vv ...T) ValidationFunc

Any will check if the provided value is in a set of allowed values.

func AnyString deprecated

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

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

Deprecated: use Any instead. Will be removed in a future release.

func BetweenNumber

func BetweenNumber[T Number](val, min, max T) ValidationFunc

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

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

func Email(val string) ValidationFunc

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

func Empty

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 Equal

func Equal[T comparable](val, exp T) ValidationFunc

Equal is a simple check to ensure that val matches exp.

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 MaxNumber

func MaxNumber[T Number](val, max T) ValidationFunc

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

func MinNumber

func MinNumber[T Number](val, min T) ValidationFunc

MinNumber will ensure a Number, 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 PositiveNumber

func PositiveNumber[T Number](val T) ValidationFunc

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

func StrLength

func StrLength[S ~string](val S, min, max int) ValidationFunc

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

func StrLengthExact

func StrLengthExact[S ~string](val S, 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