Documentation ¶
Overview ¶
Package validator implements value validations based on struct tags.
In code it is often necessary to validate that a given value is valid before using it for something. A typical example might be something like this.
if age < 18 { return error.New("age cannot be under 18") }
This is a simple enough example, but it can get significantly more complex, especially when dealing with structs.
l := len(strings.Trim(s.Username)) if l < 3 || l > 40 || !regexp.MatchString("^[a-zA-Z]$", s.Username) || s.Age < 18 || s.Password { return errors.New("Invalid request") }
You get the idea. Package validator allows one to define valid values as struct tags when defining a new struct type.
type NewUserRequest struct { Username string `validate:"min=3,max=40,regexp=^[a-zA-Z]*$"` Name string `validate:"nonzero"` Age int `validate:"min=18"` Password string `validate:"min=8"` }
Then validating a variable of type NewUserRequest becomes trivial.
nur := NewUserRequest{Username: "something", ...} if errs := validator.Validate(nur); errs != nil { // do something }
Builtin validator functions ¶
Here is the list of validator functions builtin in the package.
len For numeric numbers, len will simply make sure that the value is equal to the parameter given. For strings, it checks that the string length is exactly that number of characters. For slices, arrays, and maps, validates the number of items. (Usage: len=10) max For numeric numbers, max will simply make sure that the value is lesser or equal to the parameter given. For strings, it checks that the string length is at most that number of characters. For slices, arrays, and maps, validates the number of items. (Usage: max=10) min For numeric numbers, min will simply make sure that the value is greater or equal to the parameter given. For strings, it checks that the string length is at least that number of characters. For slices, arrays, and maps, validates the number of items. (Usage: min=10) nonzero This validates that the value is not zero. The appropriate zero value is given by the Go spec (e.g. for int it's 0, for string it's "", for pointers is nil, etc.). For pointers, the pointer's value is used to test for nonzero in addition to the pointer itself not being nil. To just check for not being nil, use nonnil. Usage: nonzero regexp Only valid for string types, it will validate that the value matches the regular expression provided as parameter. (Usage: regexp=^a.*b$) nonnil Validates that the given value is not nil. Usage: nonnil
Note that there are no tests to prevent conflicting validator parameters. For instance, these fields will never be valid.
... A int `validate:"max=0,min=1"` B string `validate:"len=10,regexp=^$" ...
Custom validation functions ¶
It is possible to define custom validation functions by using SetValidationFunc. First, one needs to create a validation function.
// Very simple validation func func notZZ(v interface{}, param string) error { st := reflect.ValueOf(v) if st.Kind() != reflect.String { return validate.ErrUnsupported } if st.String() == "ZZ" { return errors.New("value cannot be ZZ") } return nil }
Then one needs to add it to the list of validation funcs and give it a "tag" name.
validate.SetValidationFunc("notzz", notZZ)
Then it is possible to use the notzz validation tag. This will print "Field A error: value cannot be ZZ"
type T struct { A string `validate:"nonzero,notzz"` } t := T{"ZZ"} if errs := validator.Validate(t); errs != nil { fmt.Printf("Field A error: %s\n", errs["A"][0]) }
To use parameters, it is very similar.
// Very simple validator with parameter func notSomething(v interface{}, param string) error { st := reflect.ValueOf(v) if st.Kind() != reflect.String { return validate.ErrUnsupported } if st.String() == param { return errors.New("value cannot be " + param) } return nil }
And then the code below should print "Field A error: value cannot be ABC".
validator.SetValidationFunc("notsomething", notSomething) type T struct { A string `validate:"notsomething=ABC"` } t := T{"ABC"} if errs := validator.Validate(t); errs != nil { fmt.Printf("Field A error: %s\n", errs["A"][0]) }
As well, it is possible to overwrite builtin validation functions.
validate.SetValidationFunc("min", myMinFunc)
And you can delete a validation function by setting it to nil.
validate.SetValidationFunc("notzz", nil) validate.SetValidationFunc("nonzero", nil)
Using a non-existing validation func in a field tag will always return false and with error validate.ErrUnknownTag.
Finally, package validator also provides a helper function that can be used to validate simple variables/values.
// errs: nil errs = validator.Valid(42, "min=10, max=50") // errs: [validate.ErrZeroValue] errs = validator.Valid(nil, "nonzero") // errs: [validate.ErrMin,validate.ErrMax] errs = validator.Valid("hi", "nonzero,min=3,max=2")
Custom tag name ¶
In case there is a reason why one would not wish to use tag 'validate' (maybe due to a conflict with a different package), it is possible to tell the package to use a different tag.
validator.SetTag("valid")
Then.
Type T struct { A int `valid:"min=8, max=10"` B string `valid:"nonzero"` }
SetTag is permanent. The new tag name will be used until it is again changed with a new call to SetTag. A way to temporarily use a different tag exists.
validator.WithTag("foo").Validate(t) validator.WithTag("bar").Validate(t) // But this will go back to using 'validate' validator.Validate(t)
Multiple validators ¶
You may often need to have a different set of validation rules for different situations. In all the examples above, we only used the default validator but you could create a new one and set specific rules for it.
For instance, you might use the same struct to decode incoming JSON for a REST API but your needs will change when you're using it to, say, create a new instance in storage vs. when you need to change something.
type User struct { Username string `validate:"nonzero"` Name string `validate:"nonzero"` Age int `validate:"nonzero"` Password string `validate:"nonzero"` }
Maybe when creating a new user, you need to make sure all values in the struct are filled, but then you use the same struct to handle incoming requests to, say, change the password, in which case you only need the Username and the Password and don't care for the others. You might use two different validators.
type User struct { Username string `creating:"nonzero" chgpw:"nonzero"` Name string `creating:"nonzero"` Age int `creating:"nonzero"` Password string `creating:"nonzero" chgpw:"nonzero"` } var ( creationValidator = validator.NewValidator() chgPwValidator = validator.NewValidator() ) func init() { creationValidator.SetTag("creating") chgPwValidator.SetTag("chgpw") } ... func CreateUserHandler(w http.ResponseWriter, r *http.Request) { var u User json.NewDecoder(r.Body).Decode(&user) if errs := creationValidator.Validate(user); errs != nil { // the request did not include all of the User // struct fields, so send a http.StatusBadRequest // back or something } // create the new user } func SetNewUserPasswordHandler(w http.ResponseWriter, r *http.Request) { var u User json.NewDecoder(r.Body).Decode(&user) if errs := chgPwValidator.Validate(user); errs != nil { // the request did not Username and Password, // so send a http.StatusBadRequest // back or something } // save the new password }
It is also possible to do all of that using only the default validator as long as SetTag is always called before calling validator.Validate() or you chain the with WithTag().
Index ¶
- Variables
- func SetPrintJSON(printJSON bool)
- func SetTag(tag string)
- func SetValidationFunc(name string, vf ValidationFunc) error
- func Valid(val interface{}, tags string) error
- func Validate(v interface{}) error
- type ErrorArray
- type ErrorMap
- type TextErr
- type ValidationFunc
- type Validator
- func (mv *Validator) SetPrintJSON(printJSON bool)
- func (mv *Validator) SetTag(tag string)
- func (mv *Validator) SetValidationFunc(name string, vf ValidationFunc) error
- func (mv *Validator) Valid(val interface{}, tags string) error
- func (mv *Validator) Validate(v interface{}) error
- func (mv *Validator) WithPrintJSON(printJSON bool) *Validator
- func (mv *Validator) WithTag(tag string) *Validator
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrZeroValue is the error returned when variable has zero value // and nonzero or nonnil was specified ErrZeroValue = TextErr{errors.New("zero value")} // ErrMin is the error returned when variable is less than mininum // value specified ErrMin = TextErr{errors.New("less than min")} // ErrMax is the error returned when variable is more than // maximum specified ErrMax = TextErr{errors.New("greater than max")} // ErrLen is the error returned when length is not equal to // param specified ErrLen = TextErr{errors.New("invalid length")} // ErrRegexp is the error returned when the value does not // match the provided regular expression parameter ErrRegexp = TextErr{errors.New("regular expression mismatch")} // ErrUnsupported is the error error returned when a validation rule // is used with an unsupported variable type ErrUnsupported = TextErr{errors.New("unsupported type")} // ErrBadParameter is the error returned when an invalid parameter // is provided to a validation rule (e.g. a string where an int was // expected (max=foo,len=bar) or missing a parameter when one is required (len=)) ErrBadParameter = TextErr{errors.New("bad parameter")} // ErrUnknownTag is the error returned when an unknown tag is found ErrUnknownTag = TextErr{errors.New("unknown tag")} // ErrInvalid is the error returned when variable is invalid // (normally a nil pointer) ErrInvalid = TextErr{errors.New("invalid value")} // ErrCannotValidate is the error returned when a struct is unexported ErrCannotValidate = TextErr{errors.New("cannot validate unexported struct")} )
Functions ¶
func SetPrintJSON ¶
func SetPrintJSON(printJSON bool)
SetPrintJSON allows you to print errors with json tag names present in struct tags
func SetTag ¶
func SetTag(tag string)
SetTag allows you to change the tag name used in structs
Example ¶
This example shows you how to change the tag name
type T struct { A int `foo:"nonzero" bar:"min=10"` } t := T{5} v := validator.NewValidator() v.SetTag("foo") err := v.Validate(t) fmt.Printf("foo --> valid: %v, errs: %v\n", err == nil, err) v.SetTag("bar") err = v.Validate(t) errs := err.(validator.ErrorMap) fmt.Printf("bar --> valid: %v, errs: %v\n", err == nil, errs)
Output: foo --> valid: true, errs: <nil> bar --> valid: false, errs: A: less than min
func SetValidationFunc ¶
func SetValidationFunc(name string, vf ValidationFunc) error
SetValidationFunc sets the function to be used for a given validation constraint. Calling this function with nil vf is the same as removing the constraint function from the list.
func Valid ¶
Valid validates a value based on the provided tags and returns errors found or nil.
Example ¶
This example shows how to use the Valid helper function to validator any number of values
err := validator.Valid(42, "min=10,max=100,nonzero") fmt.Printf("42: valid=%v, errs=%v\n", err == nil, err) var ptr *int if err := validator.Valid(ptr, "nonzero"); err != nil { fmt.Println("ptr: Invalid nil pointer.") } err = validator.Valid("ABBA", "regexp=[ABC]*") fmt.Printf("ABBA: valid=%v\n", err == nil)
Output: 42: valid=true, errs=<nil> ptr: Invalid nil pointer. ABBA: valid=true
func Validate ¶
func Validate(v interface{}) error
Validate calls the Validate method on the default validator.
Example ¶
This example demonstrates a custom function to process template text. It installs the strings.Title function and uses it to Make Title Text Look Good In Our Template's Output.
// First create a struct to be validated // according to the validator tags. type ValidateExample struct { Name string `validate:"nonzero"` Description string Age int `validate:"min=18"` Email string `validate:"regexp=^[0-9a-z]+@[0-9a-z]+(\\.[0-9a-z]+)+$"` Address struct { Street string `validate:"nonzero"` City string `validate:"nonzero"` } } // Fill in some values ve := ValidateExample{ Name: "Joe Doe", // valid as it's nonzero Description: "", // valid no validation tag exists Age: 17, // invalid as age is less than required 18 } // invalid as Email won't match the regular expression ve.Email = "@not.a.valid.email" ve.Address.City = "Some City" // valid ve.Address.Street = "" // invalid err := validator.Validate(ve) if err == nil { fmt.Println("Values are valid.") } else { errs := err.(validator.ErrorMap) // See if Address was empty if errs["Address.Street"][0] == validator.ErrZeroValue { fmt.Println("Street cannot be empty.") } // Iterate through the list of fields and respective errors fmt.Println("Invalid due to fields:") // Here we have to sort the arrays to ensure map ordering does not // fail our example, typically it's ok to just range through the err // list when order is not important. var errOuts []string for f, e := range errs { errOuts = append(errOuts, fmt.Sprintf("\t - %s (%v)\n", f, e)) } // Again this part is extraneous and you should not need this in real // code. sort.Strings(errOuts) for _, str := range errOuts { fmt.Print(str) } }
Output: Street cannot be empty. Invalid due to fields: - Address.Street (zero value) - Age (less than min) - Email (regular expression mismatch)
Types ¶
type ErrorArray ¶
type ErrorArray []error
ErrorArray is a slice of errors returned by the Validate function.
func (ErrorArray) Error ¶
func (err ErrorArray) Error() string
ErrorArray implements the Error interface and returns all the errors comma seprated if errors exist.
type ErrorMap ¶
type ErrorMap map[string]ErrorArray
ErrorMap is a map which contains all errors from validating a struct.
type TextErr ¶
type TextErr struct {
Err error
}
TextErr is an error that also implements the TextMarshaller interface for serializing out to various plain text encodings. Packages creating their own custom errors should use TextErr if they're intending to use serializing formats like json, msgpack etc.
func (TextErr) MarshalText ¶
MarshalText implements the TextMarshaller
type ValidationFunc ¶
ValidationFunc is a function that receives the value of a field and a parameter used for the respective validation tag.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator implements a validator
func WithPrintJSON ¶
WithPrintJSON creates a new Validator with printJSON set to new value. It is useful to chain-call with Validate so we don't change the print option permanently: validator.WithPrintJSON(true).Validate(t)
func WithTag ¶
WithTag creates a new Validator with the new tag name. It is useful to chain-call with Validate so we don't change the tag name permanently: validator.WithTag("foo").Validate(t)
Example ¶
This example shows you how to change the tag name
type T struct { A int `foo:"nonzero" bar:"min=10"` } t := T{5} err := validator.WithTag("foo").Validate(t) fmt.Printf("foo --> valid: %v, errs: %v\n", err == nil, err) err = validator.WithTag("bar").Validate(t) fmt.Printf("bar --> valid: %v, errs: %v\n", err == nil, err)
Output: foo --> valid: true, errs: <nil> bar --> valid: false, errs: A: less than min
func (*Validator) SetPrintJSON ¶
SetPrintJSON allows you to print errors with json tag names present in struct tags
func (*Validator) SetValidationFunc ¶
func (mv *Validator) SetValidationFunc(name string, vf ValidationFunc) error
SetValidationFunc sets the function to be used for a given validation constraint. Calling this function with nil vf is the same as removing the constraint function from the list.
func (*Validator) Valid ¶
Valid validates a value based on the provided tags and returns errors found or nil.
func (*Validator) Validate ¶
Validate validates the fields of structs (included embedded structs) based on 'validator' tags and returns errors found indexed by the field name.
func (*Validator) WithPrintJSON ¶
WithPrintJSON creates a new Validator with printJSON set to new value. It is useful to chain-call with Validate so we don't change the print option permanently: validator.WithTag("foo").WithPrintJSON(true).Validate(t)