浏览代码

Refactors binding validation

Manu Mtz-Almeida 10 年之前
父节点
当前提交
fecde9fed6
共有 3 个文件被更改,包括 69 次插入28 次删除
  1. 13 28
      binding/binding.go
  2. 18 0
      binding/binding_test.go
  3. 38 0
      binding/default_validator.go

+ 13 - 28
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)
 }

+ 18 - 0
binding/binding_test.go

@@ -64,6 +64,24 @@ func TestBindingXML(t *testing.T) {
 		"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
 }
 
+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")

+ 38 - 0
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
+}