Refactors binding validation
This commit is contained in:
		| @ -4,12 +4,7 @@ | |||||||
|  |  | ||||||
| package binding | package binding | ||||||
|  |  | ||||||
| import ( | import "net/http" | ||||||
| 	"net/http" |  | ||||||
| 	"reflect" |  | ||||||
|  |  | ||||||
| 	"gopkg.in/bluesuncorp/validator.v5" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	MIMEJSON              = "application/json" | 	MIMEJSON              = "application/json" | ||||||
| @ -26,7 +21,16 @@ type Binding interface { | |||||||
| 	Bind(*http.Request, interface{}) error | 	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 ( | var ( | ||||||
| 	JSON = jsonBinding{} | 	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 { | func Validate(obj interface{}) error { | ||||||
| 	if kindOfData(obj) != reflect.Struct { | 	if Validator == nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	if err := validate.Struct(obj); err != nil { | 	return Validator.ValidateStruct(obj) | ||||||
| 		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 |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -64,6 +64,24 @@ func TestBindingXML(t *testing.T) { | |||||||
| 		"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>") | 		"<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) { | func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { | ||||||
| 	b := Form | 	b := Form | ||||||
| 	assert.Equal(t, b.Name(), "form") | 	assert.Equal(t, b.Name(), "form") | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								binding/default_validator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								binding/default_validator.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user