💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
validator/README.md

371 lines
14 KiB

10 years ago
Package validator
10 years ago
================
9 years ago
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v8/logo.png">
[![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-8.18.1-green.svg)
9 years ago
[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/530054/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v8&service=github)](https://coveralls.io/github/go-playground/validator?branch=v8)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
[![GoDoc](https://godoc.org/gopkg.in/go-playground/validator.v8?status.svg)](https://godoc.org/gopkg.in/go-playground/validator.v8)
9 years ago
![License](https://img.shields.io/dub/l/vibe-d.svg)
10 years ago
Package validator implements value validations for structs and individual fields based on tags.
8 years ago
### Announcement
Validator [v9.0.0 RC1](https://github.com/go-playground/validator/releases/tag/v9.0.0RC1) has been released, please let me know what you think [here](https://github.com/go-playground/validator/issues/250)
It has the following **unique** features:
- Cross Field and Cross Struct validations by using validation tags or custom validators.
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
- Handles type interface by determining it's underlying type prior to validation.
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
9 years ago
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
10 years ago
Installation
------------
10 years ago
Use go get.
10 years ago
go get gopkg.in/go-playground/validator.v8
10 years ago
or to update
go get -u gopkg.in/go-playground/validator.v8
10 years ago
Then import the validator package into your own code.
10 years ago
import "gopkg.in/go-playground/validator.v8"
10 years ago
Error Return Value
-------
Validation functions return type error
They return type error to avoid the issue discussed in the following, where err is always != nil:
* http://stackoverflow.com/a/29138676/3158232
* https://github.com/go-playground/validator/issues/134
validator only returns nil or ValidationErrors as type error; so in you code all you need to do
is check if the error returned is not nil, and if it's not type cast it to type ValidationErrors
like so:
```go
err := validate.Struct(mystruct)
validationErrors := err.(validator.ValidationErrors)
```
Usage and documentation
------
10 years ago
Please see http://godoc.org/gopkg.in/go-playground/validator.v8 for detailed usage docs.
##### Examples:
Struct & Field validation
```go
package main
import (
"fmt"
"gopkg.in/go-playground/validator.v8"
)
// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
var validate *validator.Validate
func main() {
config := &validator.Config{TagName: "validate"}
validate = validator.New(config)
validateStruct()
validateField()
}
func validateStruct() {
address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
}
user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
Email: "Badger.Smith@gmail.com",
FavouriteColor: "#000",
Addresses: []*Address{address},
}
// returns nil or ValidationErrors ( map[string]*FieldError )
errs := validate.Struct(user)
if errs != nil {
fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
// Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
err := errs.(validator.ValidationErrors)["User.Addresses[0].City"]
fmt.Println(err.Field) // output: City
fmt.Println(err.Tag) // output: required
fmt.Println(err.Kind) // output: string
fmt.Println(err.Type) // output: string
fmt.Println(err.Param) // output:
fmt.Println(err.Value) // output:
// from here you can create your own error messages in whatever language you wish
return
}
// save user to database
}
func validateField() {
myEmail := "joeybloggs.gmail.com"
errs := validate.Field(myEmail, "required,email")
if errs != nil {
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
return
}
// email ok, move on
}
```
Custom Field Type
```go
package main
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"gopkg.in/go-playground/validator.v8"
)
// DbBackedUser User struct
type DbBackedUser struct {
Name sql.NullString `validate:"required"`
Age sql.NullInt64 `validate:"required"`
}
func main() {
config := &validator.Config{TagName: "validate"}
validate := validator.New(config)
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
errs := validate.Struct(x)
if len(errs.(validator.ValidationErrors)) > 0 {
fmt.Printf("Errs:\n%+v\n", errs)
}
}
// ValidateValuer implements validator.CustomTypeFunc
func ValidateValuer(field reflect.Value) interface{} {
if valuer, ok := field.Interface().(driver.Valuer); ok {
val, err := valuer.Value()
if err == nil {
return val
}
// handle the error how you want
}
return nil
}
```
Struct Level Validation
```go
package main
import (
"fmt"
"reflect"
"gopkg.in/go-playground/validator.v8"
)
// User contains user information
type User struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}
var validate *validator.Validate
func main() {
config := &validator.Config{TagName: "validate"}
validate = validator.New(config)
validate.RegisterStructValidation(UserStructLevelValidation, User{})
validateStruct()
}
// UserStructLevelValidation contains custom struct level validations that don't always
// make sense at the field validation level. For Example this function validates that either
// FirstName or LastName exist; could have done that with a custom field validation but then
// would have had to add it to both fields duplicating the logic + overhead, this way it's
// only validated once.
//
// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
// hooks right into validator and you can combine with validation tags and still have a
// common error output format.
func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) {
user := structLevel.CurrentStruct.Interface().(User)
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
structLevel.ReportError(reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname")
structLevel.ReportError(reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname")
}
// plus can to more, even with different tag than "fnameorlname"
}
func validateStruct() {
address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
City: "Unknown",
}
user := &User{
FirstName: "",
LastName: "",
Age: 45,
Email: "Badger.Smith@gmail.com",
FavouriteColor: "#000",
Addresses: []*Address{address},
}
// returns nil or ValidationErrors ( map[string]*FieldError )
errs := validate.Struct(user)
if errs != nil {
fmt.Println(errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag
// Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag
err := errs.(validator.ValidationErrors)["User.FirstName"]
fmt.Println(err.Field) // output: FirstName
fmt.Println(err.Tag) // output: fnameorlname
fmt.Println(err.Kind) // output: string
fmt.Println(err.Type) // output: string
fmt.Println(err.Param) // output:
fmt.Println(err.Value) // output:
// from here you can create your own error messages in whatever language you wish
return
}
// save user to database
}
```
Benchmarks
------
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.5.3 darwin/amd64
```go
PASS
final touches + README updates fresh test of old vs new benchmarks ```go benchmark old ns/op new ns/op delta BenchmarkFieldSuccess-8 155 118 -23.87% BenchmarkFieldFailure-8 777 758 -2.45% BenchmarkFieldDiveSuccess-8 2813 2471 -12.16% BenchmarkFieldDiveFailure-8 3544 3172 -10.50% BenchmarkFieldCustomTypeSuccess-8 328 300 -8.54% BenchmarkFieldCustomTypeFailure-8 748 775 +3.61% BenchmarkFieldOrTagSuccess-8 1169 1122 -4.02% BenchmarkFieldOrTagFailure-8 1205 1167 -3.15% BenchmarkStructLevelValidationSuccess-8 670 548 -18.21% BenchmarkStructLevelValidationFailure-8 1380 558 -59.57% BenchmarkStructSimpleCustomTypeSuccess-8 889 623 -29.92% BenchmarkStructSimpleCustomTypeFailure-8 1536 1381 -10.09% BenchmarkStructPartialSuccess-8 1194 1036 -13.23% BenchmarkStructPartialFailure-8 1869 1734 -7.22% BenchmarkStructExceptSuccess-8 949 888 -6.43% BenchmarkStructExceptFailure-8 1181 1036 -12.28% BenchmarkStructSimpleCrossFieldSuccess-8 948 773 -18.46% BenchmarkStructSimpleCrossFieldFailure-8 1708 1487 -12.94% BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1608 1261 -21.58% BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2320 2055 -11.42% BenchmarkStructSimpleSuccess-8 833 519 -37.70% BenchmarkStructSimpleFailure-8 1612 1429 -11.35% BenchmarkStructSimpleSuccessParallel-8 239 146 -38.91% BenchmarkStructSimpleFailureParallel-8 664 551 -17.02% BenchmarkStructComplexSuccess-8 5025 3269 -34.95% BenchmarkStructComplexFailure-8 10166 8436 -17.02% BenchmarkStructComplexSuccessParallel-8 1591 1024 -35.64% BenchmarkStructComplexFailureParallel-8 4306 3536 -17.88% benchmark old allocs new allocs delta BenchmarkFieldSuccess-8 0 0 +0.00% BenchmarkFieldFailure-8 4 4 +0.00% BenchmarkFieldDiveSuccess-8 27 28 +3.70% BenchmarkFieldDiveFailure-8 31 32 +3.23% BenchmarkFieldCustomTypeSuccess-8 2 2 +0.00% BenchmarkFieldCustomTypeFailure-8 4 4 +0.00% BenchmarkFieldOrTagSuccess-8 1 1 +0.00% BenchmarkFieldOrTagFailure-8 6 6 +0.00% BenchmarkStructLevelValidationSuccess-8 6 5 -16.67% BenchmarkStructLevelValidationFailure-8 11 5 -54.55% BenchmarkStructSimpleCustomTypeSuccess-8 5 3 -40.00% BenchmarkStructSimpleCustomTypeFailure-8 11 9 -18.18% BenchmarkStructPartialSuccess-8 10 9 -10.00% BenchmarkStructPartialFailure-8 15 14 -6.67% BenchmarkStructExceptSuccess-8 7 7 +0.00% BenchmarkStructExceptFailure-8 10 9 -10.00% BenchmarkStructSimpleCrossFieldSuccess-8 6 4 -33.33% BenchmarkStructSimpleCrossFieldFailure-8 11 9 -18.18% BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 10 7 -30.00% BenchmarkStructSimpleCrossStructCrossFieldFailure-8 15 12 -20.00% BenchmarkStructSimpleSuccess-8 3 1 -66.67% BenchmarkStructSimpleFailure-8 11 9 -18.18% BenchmarkStructSimpleSuccessParallel-8 3 1 -66.67% BenchmarkStructSimpleFailureParallel-8 11 9 -18.18% BenchmarkStructComplexSuccess-8 32 15 -53.12% BenchmarkStructComplexFailure-8 77 60 -22.08% BenchmarkStructComplexSuccessParallel-8 32 15 -53.12% BenchmarkStructComplexFailureParallel-8 77 60 -22.08% benchmark old bytes new bytes delta BenchmarkFieldSuccess-8 0 0 +0.00% BenchmarkFieldFailure-8 432 432 +0.00% BenchmarkFieldDiveSuccess-8 384 464 +20.83% BenchmarkFieldDiveFailure-8 816 896 +9.80% BenchmarkFieldCustomTypeSuccess-8 32 32 +0.00% BenchmarkFieldCustomTypeFailure-8 432 432 +0.00% BenchmarkFieldOrTagSuccess-8 4 4 +0.00% BenchmarkFieldOrTagFailure-8 448 448 +0.00% BenchmarkStructLevelValidationSuccess-8 168 160 -4.76% BenchmarkStructLevelValidationFailure-8 632 160 -74.68% BenchmarkStructSimpleCustomTypeSuccess-8 56 36 -35.71% BenchmarkStructSimpleCustomTypeFailure-8 656 640 -2.44% BenchmarkStructPartialSuccess-8 352 272 -22.73% BenchmarkStructPartialFailure-8 808 730 -9.65% BenchmarkStructExceptSuccess-8 314 250 -20.38% BenchmarkStructExceptFailure-8 352 272 -22.73% BenchmarkStructSimpleCrossFieldSuccess-8 96 80 -16.67% BenchmarkStructSimpleCrossFieldFailure-8 552 536 -2.90% BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 136 112 -17.65% BenchmarkStructSimpleCrossStructCrossFieldFailure-8 600 576 -4.00% BenchmarkStructSimpleSuccess-8 24 4 -83.33% BenchmarkStructSimpleFailure-8 656 640 -2.44% BenchmarkStructSimpleSuccessParallel-8 24 4 -83.33% BenchmarkStructSimpleFailureParallel-8 656 640 -2.44% BenchmarkStructComplexSuccess-8 392 244 -37.76% BenchmarkStructComplexFailure-8 3745 3609 -3.63% BenchmarkStructComplexSuccessParallel-8 392 244 -37.76% BenchmarkStructComplexFailureParallel-8 3745 3609 -3.63% ```
8 years ago
BenchmarkFieldSuccess-8 20000000 118 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-8 2000000 758 ns/op 432 B/op 4 allocs/op
BenchmarkFieldDiveSuccess-8 500000 2471 ns/op 464 B/op 28 allocs/op
BenchmarkFieldDiveFailure-8 500000 3172 ns/op 896 B/op 32 allocs/op
BenchmarkFieldCustomTypeSuccess-8 5000000 300 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-8 2000000 775 ns/op 432 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-8 1000000 1122 ns/op 4 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-8 1000000 1167 ns/op 448 B/op 6 allocs/op
BenchmarkStructLevelValidationSuccess-8 3000000 548 ns/op 160 B/op 5 allocs/op
BenchmarkStructLevelValidationFailure-8 3000000 558 ns/op 160 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-8 2000000 623 ns/op 36 B/op 3 allocs/op
BenchmarkStructSimpleCustomTypeFailure-8 1000000 1381 ns/op 640 B/op 9 allocs/op
BenchmarkStructPartialSuccess-8 1000000 1036 ns/op 272 B/op 9 allocs/op
BenchmarkStructPartialFailure-8 1000000 1734 ns/op 730 B/op 14 allocs/op
BenchmarkStructExceptSuccess-8 2000000 888 ns/op 250 B/op 7 allocs/op
BenchmarkStructExceptFailure-8 1000000 1036 ns/op 272 B/op 9 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-8 2000000 773 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossFieldFailure-8 1000000 1487 ns/op 536 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1000000 1261 ns/op 112 B/op 7 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 2055 ns/op 576 B/op 12 allocs/op
BenchmarkStructSimpleSuccess-8 3000000 519 ns/op 4 B/op 1 allocs/op
BenchmarkStructSimpleFailure-8 1000000 1429 ns/op 640 B/op 9 allocs/op
BenchmarkStructSimpleSuccessParallel-8 10000000 146 ns/op 4 B/op 1 allocs/op
BenchmarkStructSimpleFailureParallel-8 2000000 551 ns/op 640 B/op 9 allocs/op
BenchmarkStructComplexSuccess-8 500000 3269 ns/op 244 B/op 15 allocs/op
BenchmarkStructComplexFailure-8 200000 8436 ns/op 3609 B/op 60 allocs/op
BenchmarkStructComplexSuccessParallel-8 1000000 1024 ns/op 244 B/op 15 allocs/op
BenchmarkStructComplexFailureParallel-8 500000 3536 ns/op 3609 B/op 60 allocs/op
```
Complimentary Software
----------------------
Here is a list of software that compliments using this library either pre or post validation.
8 years ago
* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.
* [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags.
How to Contribute
------
There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
please make your pull requests against those branches.
If the changes being proposed or requested are breaking changes, please create an issue, for discussion
or create a pull request against the highest development branch for example this package has a
v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet.
I strongly encourage everyone whom creates a custom validation function to contribute them and
help make this package even better.
License
------
10 years ago
Distributed under MIT License, please see license file in code for more details.