|
|
@ -56,278 +56,12 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa |
|
|
|
|
|
|
|
|
|
|
|
##### Examples: |
|
|
|
##### Examples: |
|
|
|
|
|
|
|
|
|
|
|
Struct & Field validation |
|
|
|
- [Simple](https://github.com/go-playground/validator/blob/v9/examples/simple/main.go) |
|
|
|
```go |
|
|
|
- [Custom Field Types](https://github.com/go-playground/validator/blob/v9/examples/custom/main.go) |
|
|
|
package main |
|
|
|
- [Struct Level](https://github.com/go-playground/validator/blob/v9/examples/struct-level/main.go) |
|
|
|
|
|
|
|
- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/v9/examples/translations/main.go) |
|
|
|
import ( |
|
|
|
- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/examples/gin-upgrading-overriding) |
|
|
|
"fmt" |
|
|
|
- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash) |
|
|
|
|
|
|
|
|
|
|
|
"gopkg.in/go-playground/validator.v9" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla' |
|
|
|
|
|
|
|
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"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// use a single instance of Validate, it caches struct info |
|
|
|
|
|
|
|
var validate *validator.Validate |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validate = validator.New() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validateStruct() |
|
|
|
|
|
|
|
validateVariable() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ) |
|
|
|
|
|
|
|
err := validate.Struct(user) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this check is only needed when your code could produce |
|
|
|
|
|
|
|
// an invalid value for validation such as interface with nil |
|
|
|
|
|
|
|
// value most including myself do not usually have code like this. |
|
|
|
|
|
|
|
if _, ok := err.(*validator.InvalidValidationError); ok { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, err := range err.(validator.ValidationErrors) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Println(err.Namespace()) |
|
|
|
|
|
|
|
fmt.Println(err.Field()) |
|
|
|
|
|
|
|
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or |
|
|
|
|
|
|
|
fmt.Println(err.StructField()) // by passing alt name to ReportError like below |
|
|
|
|
|
|
|
fmt.Println(err.Tag()) |
|
|
|
|
|
|
|
fmt.Println(err.ActualTag()) |
|
|
|
|
|
|
|
fmt.Println(err.Kind()) |
|
|
|
|
|
|
|
fmt.Println(err.Type()) |
|
|
|
|
|
|
|
fmt.Println(err.Value()) |
|
|
|
|
|
|
|
fmt.Println(err.Param()) |
|
|
|
|
|
|
|
fmt.Println() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// from here you can create your own error messages in whatever language you wish |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// save user to database |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func validateVariable() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
myEmail := "joeybloggs.gmail.com" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
errs := validate.Var(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.v9" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DbBackedUser User struct |
|
|
|
|
|
|
|
type DbBackedUser struct { |
|
|
|
|
|
|
|
Name sql.NullString `validate:"required"` |
|
|
|
|
|
|
|
Age sql.NullInt64 `validate:"required"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// use a single instance of Validate, it caches struct info |
|
|
|
|
|
|
|
var validate *validator.Validate |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validate = validator.New() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc |
|
|
|
|
|
|
|
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// build object for validation |
|
|
|
|
|
|
|
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := validate.Struct(x) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Printf("Err(s):\n%+v\n", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"gopkg.in/go-playground/validator.v9" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// use a single instance of Validate, it caches struct info |
|
|
|
|
|
|
|
var validate *validator.Validate |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validate = validator.New() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// register validation for 'User' |
|
|
|
|
|
|
|
// NOTE: only have to register a non-pointer type for 'User', validator |
|
|
|
|
|
|
|
// interanlly dereferences during it's type checks. |
|
|
|
|
|
|
|
validate.RegisterStructValidation(UserStructLevelValidation, User{}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// build 'User' info, normally posted data etc... |
|
|
|
|
|
|
|
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 InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError ) |
|
|
|
|
|
|
|
err := validate.Struct(user) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this check is only needed when your code could produce |
|
|
|
|
|
|
|
// an invalid value for validation such as interface with nil |
|
|
|
|
|
|
|
// value most including myself do not usually have code like this. |
|
|
|
|
|
|
|
if _, ok := err.(*validator.InvalidValidationError); ok { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, err := range err.(validator.ValidationErrors) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Println(err.Namespace()) |
|
|
|
|
|
|
|
fmt.Println(err.Field()) |
|
|
|
|
|
|
|
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or |
|
|
|
|
|
|
|
fmt.Println(err.StructField()) // by passing alt name to ReportError like below |
|
|
|
|
|
|
|
fmt.Println(err.Tag()) |
|
|
|
|
|
|
|
fmt.Println(err.ActualTag()) |
|
|
|
|
|
|
|
fmt.Println(err.Kind()) |
|
|
|
|
|
|
|
fmt.Println(err.Type()) |
|
|
|
|
|
|
|
fmt.Println(err.Value()) |
|
|
|
|
|
|
|
fmt.Println(err.Param()) |
|
|
|
|
|
|
|
fmt.Println() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// from here you can create your own error messages in whatever language you wish |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// save user to database |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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(sl validator.StructLevel) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
user := sl.Current().Interface().(User) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(user.FirstName) == 0 && len(user.LastName) == 0 { |
|
|
|
|
|
|
|
sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "") |
|
|
|
|
|
|
|
sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// plus can to more, even with different tag than "fnameorlname" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Benchmarks |
|
|
|
Benchmarks |
|
|
|
------ |
|
|
|
------ |
|
|
|