Codebase list golang-github-go-playground-validator-v10 / a13509d
Merge pull request #141 from bluesuncorp/v6-development Add RegisterCustomTypeFunc for easier adding of CustomTypeFunc Dean Karn 8 years ago
6 changed file(s) with 323 addition(s) and 236 deletion(s). Raw diff Collapse all Expand all
11 ================
22
33 [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 [![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
4 [![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487383/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
55 [![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6)
66 [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6)
77
137137 package main
138138
139139 import (
140 "errors"
140 "database/sql"
141 "database/sql/driver"
141142 "fmt"
142143 "reflect"
143144
144 sql "database/sql/driver"
145
146145 "gopkg.in/bluesuncorp/validator.v6"
147146 )
148147
149 var validate *validator.Validate
150
151 type valuer struct {
152 Name string
153 }
154
155 func (v valuer) Value() (sql.Value, error) {
156
157 if v.Name == "errorme" {
158 return nil, errors.New("some kind of error")
159 }
160
161 if v.Name == "blankme" {
162 return "", nil
163 }
164
165 if len(v.Name) == 0 {
166 return nil, nil
167 }
168
169 return v.Name, nil
170 }
171
172 // ValidateValuerType implements validator.CustomTypeFunc
173 func ValidateValuerType(field reflect.Value) interface{} {
174 if valuer, ok := field.Interface().(sql.Valuer); ok {
175 val, err := valuer.Value()
176 if err != nil {
177 // handle the error how you want
178 return nil
179 }
180
181 return val
182 }
183
184 return nil
148 // DbBackedUser User struct
149 type DbBackedUser struct {
150 Name sql.NullString `validate:"required"`
151 Age sql.NullInt64 `validate:"required"`
185152 }
186153
187154 func main() {
188
189 customTypes := map[reflect.Type]validator.CustomTypeFunc{}
190 customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
191 customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
192155
193156 config := validator.Config{
194157 TagName: "validate",
195158 ValidationFuncs: validator.BakedInValidators,
196 CustomTypeFuncs: customTypes,
197 }
198
199 validate = validator.New(config)
200
201 validateCustomFieldType()
202 }
203
204 func validateCustomFieldType() {
205 val := valuer{
206 Name: "blankme",
207 }
208
209 errs := validate.Field(val, "required")
210 if errs != nil {
211 fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag
212 return
213 }
214
215 // all ok
216 }
217
159 }
160
161 validate := validator.New(config)
162
163 // register all sql.Null* types to use the ValidateValuer CustomTypeFunc
164 validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
165
166 x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
167 errs := validate.Struct(x)
168
169 if len(errs) > 0 {
170 fmt.Printf("Errs:\n%+v\n", errs)
171 }
172 }
173
174 // ValidateValuer implements validator.CustomTypeFunc
175 func ValidateValuer(field reflect.Value) interface{} {
176 if valuer, ok := field.Interface().(driver.Valuer); ok {
177 val, err := valuer.Value()
178 if err == nil {
179 return val
180 }
181 // handle the error how you want
182 }
183 return nil
184 }
218185 ```
219186
220187 Benchmarks
0 package main
1
2 import (
3 "database/sql"
4 "database/sql/driver"
5 "fmt"
6 "reflect"
7
8 "gopkg.in/bluesuncorp/validator.v6"
9 )
10
11 // DbBackedUser User struct
12 type DbBackedUser struct {
13 Name sql.NullString `validate:"required"`
14 Age sql.NullInt64 `validate:"required"`
15 }
16
17 func main() {
18
19 config := validator.Config{
20 TagName: "validate",
21 ValidationFuncs: validator.BakedInValidators,
22 }
23
24 validate := validator.New(config)
25
26 // register all sql.Null* types to use the ValidateValuer CustomTypeFunc
27 validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
28
29 x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
30 errs := validate.Struct(x)
31
32 if len(errs) > 0 {
33 fmt.Printf("Errs:\n%+v\n", errs)
34 }
35 }
36
37 // ValidateValuer implements validator.CustomTypeFunc
38 func ValidateValuer(field reflect.Value) interface{} {
39 if valuer, ok := field.Interface().(driver.Valuer); ok {
40 val, err := valuer.Value()
41 if err == nil {
42 return val
43 }
44 // handle the error how you want
45 }
46 return nil
47 }
0 package main
1
2 import (
3 "errors"
4 "fmt"
5 "reflect"
6
7 sql "database/sql/driver"
8
9 "gopkg.in/bluesuncorp/validator.v6"
10 )
11
12 // User contains user information
13 type User struct {
14 FirstName string `validate:"required"`
15 LastName string `validate:"required"`
16 Age uint8 `validate:"gte=0,lte=130"`
17 Email string `validate:"required,email"`
18 FavouriteColor string `validate:"hexcolor|rgb|rgba"`
19 Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
20 }
21
22 // Address houses a users address information
23 type Address struct {
24 Street string `validate:"required"`
25 City string `validate:"required"`
26 Planet string `validate:"required"`
27 Phone string `validate:"required"`
28 }
29
30 var validate *validator.Validate
31
32 func main() {
33
34 config := validator.Config{
35 TagName: "validate",
36 ValidationFuncs: validator.BakedInValidators,
37 }
38
39 validate = validator.New(config)
40
41 validateStruct()
42 validateField()
43 }
44
45 func validateStruct() {
46
47 address := &Address{
48 Street: "Eavesdown Docks",
49 Planet: "Persphone",
50 Phone: "none",
51 }
52
53 user := &User{
54 FirstName: "Badger",
55 LastName: "Smith",
56 Age: 135,
57 Email: "Badger.Smith@gmail.com",
58 FavouriteColor: "#000",
59 Addresses: []*Address{address},
60 }
61
62 // returns nil or ValidationErrors ( map[string]*FieldError )
63 errs := validate.Struct(user)
64
65 if errs != nil {
66
67 fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
68 // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
69 err := errs["User.Addresses[0].City"]
70 fmt.Println(err.Field) // output: City
71 fmt.Println(err.Tag) // output: required
72 fmt.Println(err.Kind) // output: string
73 fmt.Println(err.Type) // output: string
74 fmt.Println(err.Param) // output:
75 fmt.Println(err.Value) // output:
76
77 // from here you can create your own error messages in whatever language you wish
78 return
79 }
80
81 // save user to database
82 }
83
84 func validateField() {
85 myEmail := "joeybloggs.gmail.com"
86
87 errs := validate.Field(myEmail, "required,email")
88
89 if errs != nil {
90 fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
91 return
92 }
93
94 // email ok, move on
95 }
96
97 var validate2 *validator.Validate
98
99 type valuer struct {
100 Name string
101 }
102
103 func (v valuer) Value() (sql.Value, error) {
104
105 if v.Name == "errorme" {
106 return nil, errors.New("some kind of error")
107 }
108
109 if v.Name == "blankme" {
110 return "", nil
111 }
112
113 if len(v.Name) == 0 {
114 return nil, nil
115 }
116
117 return v.Name, nil
118 }
119
120 // ValidateValuerType implements validator.CustomTypeFunc
121 func ValidateValuerType(field reflect.Value) interface{} {
122 if valuer, ok := field.Interface().(sql.Valuer); ok {
123 val, err := valuer.Value()
124 if err != nil {
125 // handle the error how you want
126 return nil
127 }
128
129 return val
130 }
131
132 return nil
133 }
134
135 func main2() {
136
137 customTypes := map[reflect.Type]validator.CustomTypeFunc{}
138 customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
139 customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
140
141 config := validator.Config{
142 TagName: "validate",
143 ValidationFuncs: validator.BakedInValidators,
144 CustomTypeFuncs: customTypes,
145 }
146
147 validate2 = validator.New(config)
148
149 validateCustomFieldType()
150 }
151
152 func validateCustomFieldType() {
153 val := valuer{
154 Name: "blankme",
155 }
156
157 errs := validate2.Field(val, "required")
158 if errs != nil {
159 fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag
160 return
161 }
162
163 // all ok
164 }
+0
-165
examples/simple.go less more
0 package main
1
2 import (
3 "errors"
4 "fmt"
5 "reflect"
6
7 sql "database/sql/driver"
8
9 "gopkg.in/bluesuncorp/validator.v6"
10 )
11
12 // User contains user information
13 type User struct {
14 FirstName string `validate:"required"`
15 LastName string `validate:"required"`
16 Age uint8 `validate:"gte=0,lte=130"`
17 Email string `validate:"required,email"`
18 FavouriteColor string `validate:"hexcolor|rgb|rgba"`
19 Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
20 }
21
22 // Address houses a users address information
23 type Address struct {
24 Street string `validate:"required"`
25 City string `validate:"required"`
26 Planet string `validate:"required"`
27 Phone string `validate:"required"`
28 }
29
30 var validate *validator.Validate
31
32 func main() {
33
34 config := validator.Config{
35 TagName: "validate",
36 ValidationFuncs: validator.BakedInValidators,
37 }
38
39 validate = validator.New(config)
40
41 validateStruct()
42 validateField()
43 }
44
45 func validateStruct() {
46
47 address := &Address{
48 Street: "Eavesdown Docks",
49 Planet: "Persphone",
50 Phone: "none",
51 }
52
53 user := &User{
54 FirstName: "Badger",
55 LastName: "Smith",
56 Age: 135,
57 Email: "Badger.Smith@gmail.com",
58 FavouriteColor: "#000",
59 Addresses: []*Address{address},
60 }
61
62 // returns nil or ValidationErrors ( map[string]*FieldError )
63 errs := validate.Struct(user)
64
65 if errs != nil {
66
67 fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
68 // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
69 err := errs["User.Addresses[0].City"]
70 fmt.Println(err.Field) // output: City
71 fmt.Println(err.Tag) // output: required
72 fmt.Println(err.Kind) // output: string
73 fmt.Println(err.Type) // output: string
74 fmt.Println(err.Param) // output:
75 fmt.Println(err.Value) // output:
76
77 // from here you can create your own error messages in whatever language you wish
78 return
79 }
80
81 // save user to database
82 }
83
84 func validateField() {
85 myEmail := "joeybloggs.gmail.com"
86
87 errs := validate.Field(myEmail, "required,email")
88
89 if errs != nil {
90 fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
91 return
92 }
93
94 // email ok, move on
95 }
96
97 var validate2 *validator.Validate
98
99 type valuer struct {
100 Name string
101 }
102
103 func (v valuer) Value() (sql.Value, error) {
104
105 if v.Name == "errorme" {
106 return nil, errors.New("some kind of error")
107 }
108
109 if v.Name == "blankme" {
110 return "", nil
111 }
112
113 if len(v.Name) == 0 {
114 return nil, nil
115 }
116
117 return v.Name, nil
118 }
119
120 // ValidateValuerType implements validator.CustomTypeFunc
121 func ValidateValuerType(field reflect.Value) interface{} {
122 if valuer, ok := field.Interface().(sql.Valuer); ok {
123 val, err := valuer.Value()
124 if err != nil {
125 // handle the error how you want
126 return nil
127 }
128
129 return val
130 }
131
132 return nil
133 }
134
135 func main2() {
136
137 customTypes := map[reflect.Type]validator.CustomTypeFunc{}
138 customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
139 customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
140
141 config := validator.Config{
142 TagName: "validate",
143 ValidationFuncs: validator.BakedInValidators,
144 CustomTypeFuncs: customTypes,
145 }
146
147 validate2 = validator.New(config)
148
149 validateCustomFieldType()
150 }
151
152 func validateCustomFieldType() {
153 val := valuer{
154 Name: "blankme",
155 }
156
157 errs := validate2.Field(val, "required")
158 if errs != nil {
159 fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag
160 return
161 }
162
163 // all ok
164 }
156156 return nil
157157 }
158158
159 // RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
160 func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
161
162 if v.config.CustomTypeFuncs == nil {
163 v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{}
164 }
165
166 for _, t := range types {
167 v.config.CustomTypeFuncs[reflect.TypeOf(t)] = fn
168 }
169
170 v.config.hasCustomFuncs = true
171 }
172
159173 // Field validates a single field using tag style validation and returns ValidationErrors
160174 // NOTE: it returns ValidationErrors instead of a single FieldError because this can also
161175 // validate Array, Slice and maps fields which may contain more than one error
00 package validator
11
22 import (
3 "database/sql"
4 "database/sql/driver"
35 "errors"
46 "fmt"
57 "reflect"
68 "testing"
79 "time"
8
9 sql "database/sql/driver"
1010
1111 . "gopkg.in/bluesuncorp/assert.v1"
1212 )
125125 Name string
126126 }
127127
128 func (v valuer) Value() (sql.Value, error) {
128 func (v valuer) Value() (driver.Value, error) {
129129
130130 if v.Name == "errorme" {
131131 return nil, errors.New("some kind of error")
177177 }
178178
179179 func ValidateValuerType(field reflect.Value) interface{} {
180 if valuer, ok := field.Interface().(sql.Valuer); ok {
180 if valuer, ok := field.Interface().(driver.Valuer); ok {
181181 val, err := valuer.Value()
182182 if err != nil {
183183 // handle the error how you want
190190 return nil
191191 }
192192
193 func TestSQLValue2Validation(t *testing.T) {
194
195 config := Config{
196 TagName: "validate",
197 ValidationFuncs: BakedInValidators,
198 }
199
200 validate := New(config)
201 validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
202 validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{})
203 validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1)
204
205 val := valuer{
206 Name: "",
207 }
208
209 errs := validate.Field(val, "required")
210 NotEqual(t, errs, nil)
211 AssertError(t, errs, "", "", "required")
212
213 val.Name = "Valid Name"
214 errs = validate.Field(val, "required")
215 Equal(t, errs, nil)
216
217 val.Name = "errorme"
218
219 PanicMatches(t, func() { errs = validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error")
220
221 type myValuer valuer
222
223 myVal := valuer{
224 Name: "",
225 }
226
227 errs = validate.Field(myVal, "required")
228 NotEqual(t, errs, nil)
229 AssertError(t, errs, "", "", "required")
230
231 cust := MadeUpCustomType{
232 FirstName: "Joey",
233 LastName: "Bloggs",
234 }
235
236 c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2}
237
238 errs = validate.Struct(c)
239 Equal(t, errs, nil)
240
241 c.MadeUp.FirstName = ""
242 c.OverriddenInt = 1
243
244 errs = validate.Struct(c)
245 NotEqual(t, errs, nil)
246 Equal(t, len(errs), 2)
247 AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required")
248 AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt")
249 }
250
193251 func TestSQLValueValidation(t *testing.T) {
194252
195253 customTypes := map[reflect.Type]CustomTypeFunc{}
196 customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
254 customTypes[reflect.TypeOf((*driver.Valuer)(nil))] = ValidateValuerType
197255 customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
198256 customTypes[reflect.TypeOf(MadeUpCustomType{})] = ValidateCustomType
199257 customTypes[reflect.TypeOf(1)] = OverrideIntTypeForSomeReason