diff --git a/binding/binding.go b/binding/binding.go
index 5ca6dc7b..968e898e 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -4,12 +4,7 @@
package binding
-import (
- "net/http"
- "reflect"
-
- "gopkg.in/bluesuncorp/validator.v5"
-)
+import "net/http"
const (
MIMEJSON = "application/json"
@@ -26,7 +21,16 @@ type Binding interface {
Bind(*http.Request, interface{}) error
}
-var validate = validator.New("binding", validator.BakedInValidators)
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is not a struct, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+}
+
+var Validator StructValidator = &defaultValidator{}
var (
JSON = jsonBinding{}
@@ -49,28 +53,9 @@ func Default(method, contentType string) Binding {
}
}
-func ValidateField(f interface{}, tag string) error {
- if err := validate.Field(f, tag); err != nil {
- return error(err)
- }
- return nil
-}
-
func Validate(obj interface{}) error {
- if kindOfData(obj) != reflect.Struct {
+ if Validator == nil {
return nil
}
- if err := validate.Struct(obj); err != nil {
- return error(err)
- }
- return nil
-}
-
-func kindOfData(data interface{}) reflect.Kind {
- value := reflect.ValueOf(data)
- valueType := value.Kind()
- if valueType == reflect.Ptr {
- valueType = value.Elem().Kind()
- }
- return valueType
+ return Validator.ValidateStruct(obj)
}
diff --git a/binding/binding_test.go b/binding/binding_test.go
index 75dda21e..db1678e4 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -64,6 +64,24 @@ func TestBindingXML(t *testing.T) {
"", "")
}
+func TestValidationFails(t *testing.T) {
+ var obj FooStruct
+ req := requestWithBody("POST", "/", `{"bar": "foo"}`)
+ err := JSON.Bind(req, &obj)
+ assert.Error(t, err)
+}
+
+func TestValidationDisabled(t *testing.T) {
+ backup := Validator
+ Validator = nil
+ defer func() { Validator = backup }()
+
+ var obj FooStruct
+ req := requestWithBody("POST", "/", `{"bar": "foo"}`)
+ err := JSON.Bind(req, &obj)
+ assert.NoError(t, err)
+}
+
func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
diff --git a/binding/default_validator.go b/binding/default_validator.go
new file mode 100644
index 00000000..fe29222d
--- /dev/null
+++ b/binding/default_validator.go
@@ -0,0 +1,38 @@
+package binding
+
+import (
+ "reflect"
+ "sync"
+
+ "gopkg.in/bluesuncorp/validator.v5"
+)
+
+type defaultValidator struct {
+ once sync.Once
+ validate *validator.Validate
+}
+
+var _ StructValidator = &defaultValidator{}
+
+func (v *defaultValidator) ValidateStruct(obj interface{}) error {
+ if kindOfData(obj) == reflect.Struct {
+ v.lazyinit()
+ return v.validate.Struct(obj)
+ }
+ return nil
+}
+
+func (v *defaultValidator) lazyinit() {
+ v.once.Do(func() {
+ v.validate = validator.New("binding", validator.BakedInValidators)
+ })
+}
+
+func kindOfData(data interface{}) reflect.Kind {
+ value := reflect.ValueOf(data)
+ valueType := value.Kind()
+ if valueType == reflect.Ptr {
+ valueType = value.Elem().Kind()
+ }
+ return valueType
+}