diff --git a/README.md b/README.md index 948ecec..a14204b 100644 --- a/README.md +++ b/README.md @@ -56,278 +56,12 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa ##### Examples: -Struct & Field validation -```go -package main - -import ( - "fmt" - - "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" -} -``` +- [Simple](https://github.com/go-playground/validator/blob/v9/examples/simple/main.go) +- [Custom Field Types](https://github.com/go-playground/validator/blob/v9/examples/custom/main.go) +- [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) +- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/examples/gin-upgrading-overriding) +- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash) Benchmarks ------ diff --git a/examples/translations/main.go b/examples/translations/main.go index 9e3d106..fa23165 100644 --- a/examples/translations/main.go +++ b/examples/translations/main.go @@ -3,7 +3,10 @@ package main import ( "fmt" + "github.com/go-playground/locales/en" + "github.com/go-playground/universal-translator" "gopkg.in/go-playground/validator.v9" + en_translations "gopkg.in/go-playground/validator.v9/translations/en" ) // User contains user information @@ -24,78 +27,103 @@ type Address struct { Phone string `validate:"required"` } -// use a single instance of Validate, it caches struct info -var validate *validator.Validate +// use a single instance , it caches struct info +var ( + uni *ut.UniversalTranslator + validate *validator.Validate +) func main() { + // NOTE: ommitting allot of error checking for brevity + + en := en.New() + uni = ut.New(en, en) + + // this is usually know or extracted from http 'Accept-Language' header + // also see uni.FindTranslator(...) + trans, _ := uni.GetTranslator("en") + validate = validator.New() + en_translations.RegisterDefaultTranslations(validate, trans) - validateStruct() - validateVariable() + translateAll(trans) + translateIndividual(trans) + translateOverride(trans) // yep you can specify your own in whatever locale you want! } -func validateStruct() { +func translateAll(trans ut.Translator) { - address := &Address{ - Street: "Eavesdown Docks", - Planet: "Persphone", - Phone: "none", + type User struct { + Username string `validate:"required"` + Tagline string `validate:"required,lt=10"` + Tagline2 string `validate:"required,gt=1"` } - user := &User{ - FirstName: "Badger", - LastName: "Smith", - Age: 135, - Email: "Badger.Smith@gmail.com", - FavouriteColor: "#000-", - Addresses: []*Address{address}, + user := User{ + Username: "Joeybloggs", + Tagline: "This tagline is way too long.", + Tagline2: "1", } - // 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 - } + // translate all error at once + errs := err.(validator.ValidationErrors) - 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() - } + // returns a map with key = namespace & value = translated error + // NOTICE: 2 errors are returned and you'll see something surprising + // translations are i18n aware!!!! + // eg. '10 characters' vs '1 character' + fmt.Println(errs.Translate(trans)) + } +} + +func translateIndividual(trans ut.Translator) { - // from here you can create your own error messages in whatever language you wish - return + type User struct { + Username string `validate:"required"` } - // save user to database + var user User + + err := validate.Struct(user) + if err != nil { + + errs := err.(validator.ValidationErrors) + + for _, e := range errs { + // can translate each error one at a time. + fmt.Println(e.Translate(trans)) + } + } } -func validateVariable() { +func translateOverride(trans ut.Translator) { - myEmail := "joeybloggs.gmail.com" + validate.RegisterTranslation("required", trans, func(ut ut.Translator) error { + return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("required", fe.Field()) - errs := validate.Var(myEmail, "required,email") + return t + }) - if errs != nil { - fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag - return + type User struct { + Username string `validate:"required"` } - // email ok, move on + var user User + + err := validate.Struct(user) + if err != nil { + + errs := err.(validator.ValidationErrors) + + for _, e := range errs { + // can translate each error one at a time. + fmt.Println(e.Translate(trans)) + } + } }