Skip to content

Commit 0f6f568

Browse files
author
Dean Karn
authored
Add contextual validation support via context.Context (#296)
* Add contextual validation support via context.Context Added: - RegisterValidationCtx - RegisterStructValidationCtx - StructCtx - StructFilteredCtx - StructPartialCtx - StructExceptCtx - VarCtx - VarWithValueCtx
1 parent fb68f39 commit 0f6f568

File tree

7 files changed

+255
-100
lines changed

7 files changed

+255
-100
lines changed

README.md

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -65,60 +65,61 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa
6565

6666
Benchmarks
6767
------
68-
###### Run on i5-7600 16 GB DDR4-2400 using Go version go1.8 linux/amd64
68+
###### Run on Dell XPS 15 i7-7700HQ 32GB Go version go1.8.3 linux/amd64
6969
```go
70-
BenchmarkFieldSuccess-4 20000000 74.3 ns/op 0 B/op 0 allocs/op
71-
BenchmarkFieldSuccessParallel-4 50000000 31.5 ns/op 0 B/op 0 allocs/op
72-
BenchmarkFieldFailure-4 3000000 556 ns/op 208 B/op 4 allocs/op
73-
BenchmarkFieldFailureParallel-4 20000000 88.7 ns/op 208 B/op 4 allocs/op
74-
BenchmarkFieldDiveSuccess-4 2000000 630 ns/op 201 B/op 11 allocs/op
75-
BenchmarkFieldDiveSuccessParallel-4 10000000 173 ns/op 201 B/op 11 allocs/op
76-
BenchmarkFieldDiveFailure-4 1000000 1350 ns/op 412 B/op 16 allocs/op
77-
BenchmarkFieldDiveFailureParallel-4 5000000 250 ns/op 412 B/op 16 allocs/op
78-
BenchmarkFieldCustomTypeSuccess-4 10000000 202 ns/op 32 B/op 2 allocs/op
79-
BenchmarkFieldCustomTypeSuccessParallel-4 20000000 63.5 ns/op 32 B/op 2 allocs/op
80-
BenchmarkFieldCustomTypeFailure-4 5000000 568 ns/op 208 B/op 4 allocs/op
81-
BenchmarkFieldCustomTypeFailureParallel-4 20000000 87.5 ns/op 208 B/op 4 allocs/op
82-
BenchmarkFieldOrTagSuccess-4 2000000 703 ns/op 16 B/op 1 allocs/op
83-
BenchmarkFieldOrTagSuccessParallel-4 3000000 447 ns/op 16 B/op 1 allocs/op
84-
BenchmarkFieldOrTagFailure-4 3000000 604 ns/op 224 B/op 5 allocs/op
85-
BenchmarkFieldOrTagFailureParallel-4 5000000 353 ns/op 224 B/op 5 allocs/op
86-
BenchmarkStructLevelValidationSuccess-4 10000000 190 ns/op 32 B/op 2 allocs/op
87-
BenchmarkStructLevelValidationSuccessParallel-4 30000000 59.9 ns/op 32 B/op 2 allocs/op
88-
BenchmarkStructLevelValidationFailure-4 2000000 705 ns/op 304 B/op 8 allocs/op
89-
BenchmarkStructLevelValidationFailureParallel-4 10000000 146 ns/op 304 B/op 8 allocs/op
90-
BenchmarkStructSimpleCustomTypeSuccess-4 5000000 361 ns/op 32 B/op 2 allocs/op
91-
BenchmarkStructSimpleCustomTypeSuccessParallel-4 20000000 101 ns/op 32 B/op 2 allocs/op
92-
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1210 ns/op 424 B/op 9 allocs/op
93-
BenchmarkStructSimpleCustomTypeFailureParallel-4 10000000 196 ns/op 440 B/op 10 allocs/op
94-
BenchmarkStructFilteredSuccess-4 2000000 757 ns/op 288 B/op 9 allocs/op
95-
BenchmarkStructFilteredSuccessParallel-4 10000000 167 ns/op 288 B/op 9 allocs/op
96-
BenchmarkStructFilteredFailure-4 3000000 619 ns/op 256 B/op 7 allocs/op
97-
BenchmarkStructFilteredFailureParallel-4 10000000 134 ns/op 256 B/op 7 allocs/op
98-
BenchmarkStructPartialSuccess-4 2000000 687 ns/op 256 B/op 6 allocs/op
99-
BenchmarkStructPartialSuccessParallel-4 10000000 159 ns/op 256 B/op 6 allocs/op
100-
BenchmarkStructPartialFailure-4 1000000 1281 ns/op 480 B/op 11 allocs/op
101-
BenchmarkStructPartialFailureParallel-4 10000000 218 ns/op 480 B/op 11 allocs/op
102-
BenchmarkStructExceptSuccess-4 1000000 1041 ns/op 496 B/op 12 allocs/op
103-
BenchmarkStructExceptSuccessParallel-4 10000000 140 ns/op 240 B/op 5 allocs/op
104-
BenchmarkStructExceptFailure-4 1000000 1014 ns/op 464 B/op 10 allocs/op
105-
BenchmarkStructExceptFailureParallel-4 10000000 201 ns/op 464 B/op 10 allocs/op
106-
BenchmarkStructSimpleCrossFieldSuccess-4 5000000 364 ns/op 72 B/op 3 allocs/op
107-
BenchmarkStructSimpleCrossFieldSuccessParallel-4 20000000 103 ns/op 72 B/op 3 allocs/op
108-
BenchmarkStructSimpleCrossFieldFailure-4 2000000 789 ns/op 304 B/op 8 allocs/op
109-
BenchmarkStructSimpleCrossFieldFailureParallel-4 10000000 174 ns/op 304 B/op 8 allocs/op
110-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 3000000 522 ns/op 80 B/op 4 allocs/op
111-
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-4 10000000 146 ns/op 80 B/op 4 allocs/op
112-
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 2000000 879 ns/op 320 B/op 9 allocs/op
113-
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-4 10000000 225 ns/op 320 B/op 9 allocs/op
114-
BenchmarkStructSimpleSuccess-4 10000000 223 ns/op 0 B/op 0 allocs/op
115-
BenchmarkStructSimpleSuccessParallel-4 20000000 63.3 ns/op 0 B/op 0 allocs/op
116-
BenchmarkStructSimpleFailure-4 2000000 1097 ns/op 424 B/op 9 allocs/op
117-
BenchmarkStructSimpleFailureParallel-4 10000000 182 ns/op 424 B/op 9 allocs/op
118-
BenchmarkStructComplexSuccess-4 1000000 1362 ns/op 128 B/op 8 allocs/op
119-
BenchmarkStructComplexSuccessParallel-4 5000000 359 ns/op 128 B/op 8 allocs/op
120-
BenchmarkStructComplexFailure-4 300000 6446 ns/op 3040 B/op 53 allocs/op
121-
BenchmarkStructComplexFailureParallel-4 1000000 1203 ns/op 3040 B/op 53 allocs/op
70+
go test -run=XXX -bench=. -benchmem=true
71+
BenchmarkFieldSuccess-8 20000000 88.3 ns/op 0 B/op 0 allocs/op
72+
BenchmarkFieldSuccessParallel-8 50000000 30.4 ns/op 0 B/op 0 allocs/op
73+
BenchmarkFieldFailure-8 3000000 428 ns/op 208 B/op 4 allocs/op
74+
BenchmarkFieldFailureParallel-8 20000000 96.0 ns/op 208 B/op 4 allocs/op
75+
BenchmarkFieldDiveSuccess-8 2000000 695 ns/op 201 B/op 11 allocs/op
76+
BenchmarkFieldDiveSuccessParallel-8 10000000 205 ns/op 201 B/op 11 allocs/op
77+
BenchmarkFieldDiveFailure-8 1000000 1083 ns/op 412 B/op 16 allocs/op
78+
BenchmarkFieldDiveFailureParallel-8 5000000 278 ns/op 413 B/op 16 allocs/op
79+
BenchmarkFieldCustomTypeSuccess-8 10000000 229 ns/op 32 B/op 2 allocs/op
80+
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 72.4 ns/op 32 B/op 2 allocs/op
81+
BenchmarkFieldCustomTypeFailure-8 5000000 377 ns/op 208 B/op 4 allocs/op
82+
BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.0 ns/op 208 B/op 4 allocs/op
83+
BenchmarkFieldOrTagSuccess-8 2000000 767 ns/op 16 B/op 1 allocs/op
84+
BenchmarkFieldOrTagSuccessParallel-8 3000000 425 ns/op 16 B/op 1 allocs/op
85+
BenchmarkFieldOrTagFailure-8 2000000 548 ns/op 224 B/op 5 allocs/op
86+
BenchmarkFieldOrTagFailureParallel-8 3000000 411 ns/op 224 B/op 5 allocs/op
87+
BenchmarkStructLevelValidationSuccess-8 10000000 219 ns/op 32 B/op 2 allocs/op
88+
BenchmarkStructLevelValidationSuccessParallel-8 20000000 69.2 ns/op 32 B/op 2 allocs/op
89+
BenchmarkStructLevelValidationFailure-8 2000000 628 ns/op 304 B/op 8 allocs/op
90+
BenchmarkStructLevelValidationFailureParallel-8 10000000 165 ns/op 304 B/op 8 allocs/op
91+
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 411 ns/op 32 B/op 2 allocs/op
92+
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 122 ns/op 32 B/op 2 allocs/op
93+
BenchmarkStructSimpleCustomTypeFailure-8 1000000 1022 ns/op 424 B/op 9 allocs/op
94+
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 228 ns/op 440 B/op 10 allocs/op
95+
BenchmarkStructFilteredSuccess-8 2000000 737 ns/op 288 B/op 9 allocs/op
96+
BenchmarkStructFilteredSuccessParallel-8 10000000 192 ns/op 288 B/op 9 allocs/op
97+
BenchmarkStructFilteredFailure-8 3000000 583 ns/op 256 B/op 7 allocs/op
98+
BenchmarkStructFilteredFailureParallel-8 10000000 152 ns/op 256 B/op 7 allocs/op
99+
BenchmarkStructPartialSuccess-8 2000000 731 ns/op 256 B/op 6 allocs/op
100+
BenchmarkStructPartialSuccessParallel-8 10000000 173 ns/op 256 B/op 6 allocs/op
101+
BenchmarkStructPartialFailure-8 1000000 1164 ns/op 480 B/op 11 allocs/op
102+
BenchmarkStructPartialFailureParallel-8 5000000 253 ns/op 480 B/op 11 allocs/op
103+
BenchmarkStructExceptSuccess-8 1000000 1337 ns/op 496 B/op 12 allocs/op
104+
BenchmarkStructExceptSuccessParallel-8 10000000 153 ns/op 240 B/op 5 allocs/op
105+
BenchmarkStructExceptFailure-8 2000000 954 ns/op 464 B/op 10 allocs/op
106+
BenchmarkStructExceptFailureParallel-8 5000000 234 ns/op 464 B/op 10 allocs/op
107+
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 420 ns/op 72 B/op 3 allocs/op
108+
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 125 ns/op 72 B/op 3 allocs/op
109+
BenchmarkStructSimpleCrossFieldFailure-8 2000000 790 ns/op 304 B/op 8 allocs/op
110+
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 205 ns/op 304 B/op 8 allocs/op
111+
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 611 ns/op 80 B/op 4 allocs/op
112+
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 172 ns/op 80 B/op 4 allocs/op
113+
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1112 ns/op 320 B/op 9 allocs/op
114+
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 258 ns/op 320 B/op 9 allocs/op
115+
BenchmarkStructSimpleSuccess-8 5000000 263 ns/op 0 B/op 0 allocs/op
116+
BenchmarkStructSimpleSuccessParallel-8 20000000 83.1 ns/op 0 B/op 0 allocs/op
117+
BenchmarkStructSimpleFailure-8 2000000 964 ns/op 424 B/op 9 allocs/op
118+
BenchmarkStructSimpleFailureParallel-8 10000000 212 ns/op 424 B/op 9 allocs/op
119+
BenchmarkStructComplexSuccess-8 1000000 1504 ns/op 128 B/op 8 allocs/op
120+
BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op
121+
BenchmarkStructComplexFailure-8 300000 7585 ns/op 3041 B/op 53 allocs/op
122+
BenchmarkStructComplexFailureParallel-8 1000000 1387 ns/op 3041 B/op 53 allocs/op
122123
```
123124

124125
Complementary Software

baked_in.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package validator
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"net/url"
@@ -10,13 +11,22 @@ import (
1011
"unicode/utf8"
1112
)
1213

13-
// Func accepts all values needed for file and cross field validation
14-
// fl = FieldLevel validation helper
15-
// field = field value for validation
16-
// fieldType = fields
17-
// param = parameter used in validation i.e. gt=0 param would be 0
14+
// Func accepts a FieldLevel interface for all validation needs
1815
type Func func(fl FieldLevel) bool
1916

17+
// FuncCtx accepts a context.Context and FieldLevel interface for all validation needs
18+
type FuncCtx func(ctx context.Context, fl FieldLevel) bool
19+
20+
// wrapFunc wraps noramal Func makes it compatible with FuncCtx
21+
func wrapFunc(fn Func) FuncCtx {
22+
if fn == nil {
23+
return nil // be sure not to wrap a bad function.
24+
}
25+
return func(ctx context.Context, fl FieldLevel) bool {
26+
return fn(fl)
27+
}
28+
}
29+
2030
var (
2131
restrictedTags = map[string]struct{}{
2232
diveTag: {},

cache.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (tc *tagCache) Set(key string, value *cTag) {
7171
type cStruct struct {
7272
name string
7373
fields []*cField
74-
fn StructLevelFunc
74+
fn StructLevelFuncCtx
7575
}
7676

7777
type cField struct {
@@ -90,7 +90,7 @@ type cTag struct {
9090
hasAlias bool
9191
typeof tagType
9292
hasTag bool
93-
fn Func
93+
fn FuncCtx
9494
next *cTag
9595
}
9696

struct_level.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
package validator
22

3-
import "reflect"
3+
import (
4+
"context"
5+
"reflect"
6+
)
47

58
// StructLevelFunc accepts all values needed for struct level validation
69
type StructLevelFunc func(sl StructLevel)
710

11+
// StructLevelFuncCtx accepts all values needed for struct level validation
12+
// but also allows passing of contextual validation information vi context.Context.
13+
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
14+
15+
// wrapStructLevelFunc wraps noramal StructLevelFunc makes it compatible with StructLevelFuncCtx
16+
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
17+
return func(ctx context.Context, sl StructLevel) {
18+
fn(sl)
19+
}
20+
}
21+
822
// StructLevel contains all the information and helper functions
923
// to validate a struct
1024
type StructLevel interface {
@@ -21,8 +35,6 @@ type StructLevel interface {
2135
Parent() reflect.Value
2236

2337
// returns the current struct.
24-
// this is not needed when implementing 'Validatable' interface,
25-
// only when a StructLevel is registered
2638
Current() reflect.Value
2739

2840
// ExtractType gets the actual underlying type of field value.

validator.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package validator
22

33
import (
4+
"context"
45
"fmt"
56
"reflect"
67
"strconv"
@@ -34,7 +35,7 @@ type validate struct {
3435
}
3536

3637
// parent and current will be the same the first run of validateStruct
37-
func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
38+
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
3839

3940
cs, ok := v.v.structCache.Get(typ)
4041
if !ok {
@@ -78,7 +79,7 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
7879
}
7980
}
8081

81-
v.traverseField(parent, current.Field(f.idx), ns, structNs, f, f.cTags)
82+
v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags)
8283
}
8384
}
8485

@@ -92,12 +93,12 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
9293
v.ns = ns
9394
v.actualNs = structNs
9495

95-
cs.fn(v)
96+
cs.fn(ctx, v)
9697
}
9798
}
9899

99100
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
100-
func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
101+
func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
101102

102103
var typ reflect.Type
103104
var kind reflect.Kind
@@ -192,7 +193,7 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
192193
structNs = append(append(structNs, cf.name...), '.')
193194
}
194195

195-
v.validateStruct(current, current, typ, ns, structNs, ct)
196+
v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
196197
return
197198
}
198199
}
@@ -261,7 +262,7 @@ OUTER:
261262
reusableCF.altName = string(v.misc)
262263
}
263264

264-
v.traverseField(parent, current.Index(i), ns, structNs, reusableCF, ct)
265+
v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
265266
}
266267

267268
case reflect.Map:
@@ -291,7 +292,7 @@ OUTER:
291292
reusableCF.altName = string(v.misc)
292293
}
293294

294-
v.traverseField(parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
295+
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
295296
}
296297

297298
default:
@@ -314,7 +315,7 @@ OUTER:
314315
v.cf = cf
315316
v.ct = ct
316317

317-
if ct.fn(v) {
318+
if ct.fn(ctx, v) {
318319

319320
// drain rest of the 'or' values, then continue or leave
320321
for {
@@ -407,7 +408,7 @@ OUTER:
407408
// v.ns = ns
408409
// v.actualNs = structNs
409410

410-
if !ct.fn(v) {
411+
if !ct.fn(ctx, v) {
411412

412413
v.str1 = string(append(ns, cf.altName...))
413414

0 commit comments

Comments
 (0)