@@ -2,7 +2,7 @@ Package validator
22================
33<img align =" right " src =" https://raw.githubusercontent.com/go-playground/validator/v9/logo.png " >
44[ ![ 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 )
5- ![ Project status] ( https://img.shields.io/badge/alpha -9.0.0-red .svg )
5+ ![ Project status] ( https://img.shields.io/badge/RC1 -9.0.0-yellow .svg )
66[ ![ Build Status] ( https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/530054/badge.svg )] ( https://semaphoreci.com/joeybloggs/validator )
77[ ![ Coverage Status] ( https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github )] ( https://coveralls.io/github/go-playground/validator?branch=v9 )
88[ ![ Go Report Card] ( https://goreportcard.com/badge/github.com/go-playground/validator )] ( https://goreportcard.com/report/github.com/go-playground/validator )
@@ -77,7 +77,7 @@ type User struct {
7777 LastName string ` validate:"required"`
7878 Age uint8 ` validate:"gte=0,lte=130"`
7979 Email string ` validate:"required,email"`
80- FavouriteColor string ` validate:"hexcolor|rgb|rgba" `
80+ FavouriteColor string ` validate:"iscolor" ` // alias for ' hexcolor|rgb|rgba|hsl|hsla'
8181 Addresses []*Address ` validate:"required,dive,required"` // a person can have a home and cottage...
8282}
8383
@@ -89,16 +89,15 @@ type Address struct {
8989 Phone string ` validate:"required"`
9090}
9191
92+ // use a single instance of Validate, it caches struct info
9293var validate *validator.Validate
9394
9495func main () {
9596
96- config := &validator.Config {TagName: " validate" }
97-
98- validate = validator.New (config)
97+ validate = validator.New ()
9998
10099 validateStruct ()
101- validateField ()
100+ validateVariable ()
102101}
103102
104103func validateStruct () {
@@ -114,24 +113,36 @@ func validateStruct() {
114113 LastName: " Smith" ,
115114 Age: 135 ,
116115117- FavouriteColor: " #000" ,
116+ FavouriteColor: " #000- " ,
118117 Addresses: []*Address{address},
119118 }
120119
121120 // returns nil or ValidationErrors ( map[string]*FieldError )
122- errs := validate.Struct (user)
123-
124- if errs != nil {
121+ err := validate.Struct (user)
122+ if err != nil {
123+
124+ // this check is only needed when your code could produce
125+ // an invalid value for validation such as interface with nil
126+ // value most including myself do not usually have code like this.
127+ if _ , ok := err.(*validator.InvalidValidationError ); ok {
128+ fmt.Println (err)
129+ return
130+ }
125131
126- fmt.Println (errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
127- // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
128- err := errs.(validator.ValidationErrors )[" User.Addresses[0].City" ]
129- fmt.Println (err.Field ) // output: City
130- fmt.Println (err.Tag ) // output: required
131- fmt.Println (err.Kind ) // output: string
132- fmt.Println (err.Type ) // output: string
133- fmt.Println (err.Param ) // output:
134- fmt.Println (err.Value ) // output:
132+ for _ , err := range err.(validator.ValidationErrors ) {
133+
134+ fmt.Println (err.Namespace ())
135+ fmt.Println (err.Field ())
136+ fmt.Println (err.StructNamespace ()) // can differ when a custom TagNameFunc is registered or
137+ fmt.Println (err.StructField ()) // by passing alt name to ReportError like below
138+ fmt.Println (err.Tag ())
139+ fmt.Println (err.ActualTag ())
140+ fmt.Println (err.Kind ())
141+ fmt.Println (err.Type ())
142+ fmt.Println (err.Value ())
143+ fmt.Println (err.Param ())
144+ fmt.Println ()
145+ }
135146
136147 // from here you can create your own error messages in whatever language you wish
137148 return
@@ -140,10 +151,11 @@ func validateStruct() {
140151 // save user to database
141152}
142153
143- func validateField () {
154+ func validateVariable () {
155+
144156 myEmail := " joeybloggs.gmail.com"
145157
146- errs := validate.Field (myEmail, " required,email" )
158+ errs := validate.Var (myEmail, " required,email" )
147159
148160 if errs != nil {
149161 fmt.Println (errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
@@ -164,7 +176,7 @@ import (
164176 " fmt"
165177 " reflect"
166178
167- " gopkg.in/go-playground/validator.v8 "
179+ " gopkg.in/go-playground/validator.v9 "
168180)
169181
170182// DbBackedUser User struct
@@ -173,32 +185,38 @@ type DbBackedUser struct {
173185 Age sql.NullInt64 ` validate:"required"`
174186}
175187
176- func main () {
188+ // use a single instance of Validate, it caches struct info
189+ var validate *validator.Validate
177190
178- config := &validator. Config {TagName: " validate " }
191+ func main () {
179192
180- validate : = validator.New (config )
193+ validate = validator.New ()
181194
182195 // register all sql.Null* types to use the ValidateValuer CustomTypeFunc
183196 validate.RegisterCustomTypeFunc (ValidateValuer, sql.NullString {}, sql.NullInt64 {}, sql.NullBool {}, sql.NullFloat64 {})
184197
198+ // build object for validation
185199 x := DbBackedUser{Name: sql.NullString {String: " " , Valid: true }, Age: sql.NullInt64 {Int64: 0 , Valid: false }}
186- errs := validate.Struct (x)
187200
188- if len (errs.(validator.ValidationErrors )) > 0 {
189- fmt.Printf (" Errs:\n %+v \n " , errs)
201+ err := validate.Struct (x)
202+
203+ if err != nil {
204+ fmt.Printf (" Err(s):\n %+v \n " , err)
190205 }
191206}
192207
193208// ValidateValuer implements validator.CustomTypeFunc
194209func ValidateValuer (field reflect .Value ) interface {} {
210+
195211 if valuer , ok := field.Interface ().(driver.Valuer ); ok {
212+
196213 val , err := valuer.Value ()
197214 if err == nil {
198215 return val
199216 }
200217 // handle the error how you want
201218 }
219+
202220 return nil
203221}
204222```
@@ -209,7 +227,6 @@ package main
209227
210228import (
211229 " fmt"
212- " reflect"
213230
214231 " gopkg.in/go-playground/validator.v9"
215232)
@@ -232,41 +249,19 @@ type Address struct {
232249 Phone string ` validate:"required"`
233250}
234251
252+ // use a single instance of Validate, it caches struct info
235253var validate *validator.Validate
236254
237255func main () {
238256
239- config := & validator.Config {TagName: " validate " }
257+ validate = validator.New ()
240258
241- validate = validator.New (config)
259+ // register validation for 'User'
260+ // NOTE: only have to register a non-pointer type for 'User', validator
261+ // interanlly dereferences during it's type checks.
242262 validate.RegisterStructValidation (UserStructLevelValidation, User{})
243263
244- validateStruct ()
245- }
246-
247- // UserStructLevelValidation contains custom struct level validations that don't always
248- // make sense at the field validation level. For Example this function validates that either
249- // FirstName or LastName exist; could have done that with a custom field validation but then
250- // would have had to add it to both fields duplicating the logic + overhead, this way it's
251- // only validated once.
252- //
253- // NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
254- // hooks right into validator and you can combine with validation tags and still have a
255- // common error output format.
256- func UserStructLevelValidation (v *validator .Validate , structLevel *validator .StructLevel ) {
257-
258- user := structLevel.CurrentStruct .Interface ().(User)
259-
260- if len (user.FirstName ) == 0 && len (user.LastName ) == 0 {
261- structLevel.ReportError (reflect.ValueOf (user.FirstName ), " FirstName" , " fname" , " fnameorlname" )
262- structLevel.ReportError (reflect.ValueOf (user.LastName ), " LastName" , " lname" , " fnameorlname" )
263- }
264-
265- // plus can to more, even with different tag than "fnameorlname"
266- }
267-
268- func validateStruct () {
269-
264+ // build 'User' info, normally posted data etc...
270265 address := &Address{
271266 Street: " Eavesdown Docks" ,
272267 Planet: " Persphone" ,
@@ -283,62 +278,114 @@ func validateStruct() {
283278 Addresses: []*Address{address},
284279 }
285280
286- // returns nil or ValidationErrors ( map[string]*FieldError )
287- errs := validate.Struct (user)
281+ // returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
282+ err := validate.Struct (user)
283+ if err != nil {
288284
289- if errs != nil {
285+ // this check is only needed when your code could produce
286+ // an invalid value for validation such as interface with nil
287+ // value most including myself do not usually have code like this.
288+ if _ , ok := err.(*validator.InvalidValidationError ); ok {
289+ fmt.Println (err)
290+ return
291+ }
290292
291- fmt.Println (errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag
292- // Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag
293- err := errs.(validator.ValidationErrors )[" User.FirstName" ]
294- fmt.Println (err.Field ) // output: FirstName
295- fmt.Println (err.Tag ) // output: fnameorlname
296- fmt.Println (err.Kind ) // output: string
297- fmt.Println (err.Type ) // output: string
298- fmt.Println (err.Param ) // output:
299- fmt.Println (err.Value ) // output:
293+ for _ , err := range err.(validator.ValidationErrors ) {
294+
295+ fmt.Println (err.Namespace ())
296+ fmt.Println (err.Field ())
297+ fmt.Println (err.StructNamespace ()) // can differ when a custom TagNameFunc is registered or
298+ fmt.Println (err.StructField ()) // by passing alt name to ReportError like below
299+ fmt.Println (err.Tag ())
300+ fmt.Println (err.ActualTag ())
301+ fmt.Println (err.Kind ())
302+ fmt.Println (err.Type ())
303+ fmt.Println (err.Value ())
304+ fmt.Println (err.Param ())
305+ fmt.Println ()
306+ }
300307
301308 // from here you can create your own error messages in whatever language you wish
302309 return
303310 }
304311
305312 // save user to database
306313}
314+
315+ // UserStructLevelValidation contains custom struct level validations that don't always
316+ // make sense at the field validation level. For Example this function validates that either
317+ // FirstName or LastName exist; could have done that with a custom field validation but then
318+ // would have had to add it to both fields duplicating the logic + overhead, this way it's
319+ // only validated once.
320+ //
321+ // NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
322+ // hooks right into validator and you can combine with validation tags and still have a
323+ // common error output format.
324+ func UserStructLevelValidation (sl validator .StructLevel ) {
325+
326+ user := sl.Current ().Interface ().(User)
327+
328+ if len (user.FirstName ) == 0 && len (user.LastName ) == 0 {
329+ sl.ReportError (user.FirstName , " FirstName" , " fname" , " fnameorlname" , " " )
330+ sl.ReportError (user.LastName , " LastName" , " lname" , " fnameorlname" , " " )
331+ }
332+
333+ // plus can to more, even with different tag than "fnameorlname"
334+ }
307335```
308336
309337Benchmarks
310338------
311- ###### 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
339+ ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.6 .3 darwin/amd64
312340``` go
313- PASS
314- BenchmarkFieldSuccess-8 20000000 118 ns/op 0 B /op 0 allocs/op
315- BenchmarkFieldFailure-8 2000000 758 ns/op 432 B /op 4 allocs/op
316- BenchmarkFieldDiveSuccess-8 500000 2471 ns/op 464 B /op 28 allocs/op
317- BenchmarkFieldDiveFailure-8 500000 3172 ns/op 896 B /op 32 allocs/op
318- BenchmarkFieldCustomTypeSuccess-8 5000000 300 ns/op 32 B /op 2 allocs/op
319- BenchmarkFieldCustomTypeFailure-8 2000000 775 ns/op 432 B /op 4 allocs/op
320- BenchmarkFieldOrTagSuccess-8 1000000 1122 ns/op 4 B /op 1 allocs/op
321- BenchmarkFieldOrTagFailure-8 1000000 1167 ns/op 448 B /op 6 allocs/op
322- BenchmarkStructLevelValidationSuccess-8 3000000 548 ns/op 160 B /op 5 allocs/op
323- BenchmarkStructLevelValidationFailure-8 3000000 558 ns/op 160 B /op 5 allocs/op
324- BenchmarkStructSimpleCustomTypeSuccess-8 2000000 623 ns/op 36 B /op 3 allocs/op
325- BenchmarkStructSimpleCustomTypeFailure-8 1000000 1381 ns/op 640 B /op 9 allocs/op
326- BenchmarkStructPartialSuccess-8 1000000 1036 ns/op 272 B /op 9 allocs/op
327- BenchmarkStructPartialFailure-8 1000000 1734 ns/op 730 B /op 14 allocs/op
328- BenchmarkStructExceptSuccess-8 2000000 888 ns/op 250 B /op 7 allocs/op
329- BenchmarkStructExceptFailure-8 1000000 1036 ns/op 272 B /op 9 allocs/op
330- BenchmarkStructSimpleCrossFieldSuccess-8 2000000 773 ns/op 80 B /op 4 allocs/op
331- BenchmarkStructSimpleCrossFieldFailure-8 1000000 1487 ns/op 536 B /op 9 allocs/op
332- BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1000000 1261 ns/op 112 B /op 7 allocs/op
333- BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 2055 ns/op 576 B /op 12 allocs/op
334- BenchmarkStructSimpleSuccess-8 3000000 519 ns/op 4 B /op 1 allocs/op
335- BenchmarkStructSimpleFailure-8 1000000 1429 ns/op 640 B /op 9 allocs/op
336- BenchmarkStructSimpleSuccessParallel-8 10000000 146 ns/op 4 B /op 1 allocs/op
337- BenchmarkStructSimpleFailureParallel-8 2000000 551 ns/op 640 B /op 9 allocs/op
338- BenchmarkStructComplexSuccess-8 500000 3269 ns/op 244 B /op 15 allocs/op
339- BenchmarkStructComplexFailure-8 200000 8436 ns/op 3609 B /op 60 allocs/op
340- BenchmarkStructComplexSuccessParallel-8 1000000 1024 ns/op 244 B /op 15 allocs/op
341- BenchmarkStructComplexFailureParallel-8 500000 3536 ns/op 3609 B /op 60 allocs/op
341+ BenchmarkFieldSuccess-8 10000000 147 ns/op 0 B /op 0 allocs/op
342+ BenchmarkFieldSuccessParallel-8 30000000 42.5 ns/op 0 B /op 0 allocs/op
343+ BenchmarkFieldFailure-8 3000000 417 ns/op 192 B /op 4 allocs/op
344+ BenchmarkFieldFailureParallel-8 10000000 140 ns/op 192 B /op 4 allocs/op
345+ BenchmarkFieldDiveSuccess-8 2000000 876 ns/op 201 B /op 11 allocs/op
346+ BenchmarkFieldDiveSuccessParallel-8 5000000 277 ns/op 201 B /op 11 allocs/op
347+ BenchmarkFieldDiveFailure-8 1000000 1185 ns/op 396 B /op 16 allocs/op
348+ BenchmarkFieldDiveFailureParallel-8 3000000 402 ns/op 397 B /op 16 allocs/op
349+ BenchmarkFieldCustomTypeSuccess-8 5000000 321 ns/op 32 B /op 2 allocs/op
350+ BenchmarkFieldCustomTypeSuccessParallel-8 20000000 104 ns/op 32 B /op 2 allocs/op
351+ BenchmarkFieldCustomTypeFailure-8 3000000 416 ns/op 192 B /op 4 allocs/op
352+ BenchmarkFieldCustomTypeFailureParallel-8 10000000 150 ns/op 192 B /op 4 allocs/op
353+ BenchmarkFieldOrTagSuccess-8 1000000 1119 ns/op 16 B /op 1 allocs/op
354+ BenchmarkFieldOrTagSuccessParallel-8 3000000 462 ns/op 16 B /op 1 allocs/op
355+ BenchmarkFieldOrTagFailure-8 2000000 715 ns/op 208 B /op 5 allocs/op
356+ BenchmarkFieldOrTagFailureParallel-8 3000000 436 ns/op 208 B /op 5 allocs/op
357+ BenchmarkStructLevelValidationSuccess-8 3000000 399 ns/op 32 B /op 2 allocs/op
358+ BenchmarkStructLevelValidationSuccessParallel-8 20000000 140 ns/op 32 B /op 2 allocs/op
359+ BenchmarkStructLevelValidationFailure-8 2000000 749 ns/op 288 B /op 8 allocs/op
360+ BenchmarkStructLevelValidationFailureParallel-8 5000000 296 ns/op 288 B /op 8 allocs/op
361+ BenchmarkStructSimpleCustomTypeSuccess-8 2000000 673 ns/op 32 B /op 2 allocs/op
362+ BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 213 ns/op 32 B /op 2 allocs/op
363+ BenchmarkStructSimpleCustomTypeFailure-8 1000000 1056 ns/op 392 B /op 9 allocs/op
364+ BenchmarkStructSimpleCustomTypeFailureParallel-8 3000000 450 ns/op 408 B /op 10 allocs/op
365+ BenchmarkStructPartialSuccess-8 2000000 789 ns/op 256 B /op 6 allocs/op
366+ BenchmarkStructPartialSuccessParallel-8 5000000 307 ns/op 256 B /op 6 allocs/op
367+ BenchmarkStructPartialFailure-8 1000000 1105 ns/op 464 B /op 11 allocs/op
368+ BenchmarkStructPartialFailureParallel-8 5000000 493 ns/op 464 B /op 11 allocs/op
369+ BenchmarkStructExceptSuccess-8 1000000 1212 ns/op 480 B /op 12 allocs/op
370+ BenchmarkStructExceptSuccessParallel-8 10000000 282 ns/op 240 B /op 5 allocs/op
371+ BenchmarkStructExceptFailure-8 1000000 1004 ns/op 448 B /op 10 allocs/op
372+ BenchmarkStructExceptFailureParallel-8 5000000 452 ns/op 448 B /op 10 allocs/op
373+ BenchmarkStructSimpleCrossFieldSuccess-8 2000000 656 ns/op 72 B /op 3 allocs/op
374+ BenchmarkStructSimpleCrossFieldSuccessParallel-8 5000000 211 ns/op 72 B /op 3 allocs/op
375+ BenchmarkStructSimpleCrossFieldFailure-8 2000000 968 ns/op 288 B /op 8 allocs/op
376+ BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 406 ns/op 288 B /op 8 allocs/op
377+ BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1000000 1000 ns/op 80 B /op 4 allocs/op
378+ BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 334 ns/op 80 B /op 4 allocs/op
379+ BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1324 ns/op 304 B /op 9 allocs/op
380+ BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 520 ns/op 304 B /op 9 allocs/op
381+ BenchmarkStructSimpleSuccess-8 3000000 534 ns/op 0 B /op 0 allocs/op
382+ BenchmarkStructSimpleSuccessParallel-8 10000000 144 ns/op 0 B /op 0 allocs/op
383+ BenchmarkStructSimpleFailure-8 1000000 1039 ns/op 392 B /op 9 allocs/op
384+ BenchmarkStructSimpleFailureParallel-8 5000000 419 ns/op 392 B /op 9 allocs/op
385+ BenchmarkStructComplexSuccess-8 500000 2678 ns/op 128 B /op 8 allocs/op
386+ BenchmarkStructComplexSuccessParallel-8 2000000 874 ns/op 128 B /op 8 allocs/op
387+ BenchmarkStructComplexFailure-8 200000 6342 ns/op 2833 B /op 53 allocs/op
388+ BenchmarkStructComplexFailureParallel-8 1000000 2875 ns/op 2833 B /op 53 allocs/op
342389```
343390
344391Complimentary Software
0 commit comments