feat(binding): add support for custom validator / validation tags (#1068)
* feat(binding): Add support for custom validation tags * docs: Add example for custom validation tag * test(binding): Add test for registering custom validation
This commit is contained in:
		
				
					committed by
					
						 Javier Provecho Fernandez
						Javier Provecho Fernandez
					
				
			
			
				
	
			
			
			
						parent
						
							030b1aaf72
						
					
				
				
					commit
					26c3f42095
				
			| @ -4,7 +4,11 @@ | ||||
|  | ||||
| package binding | ||||
|  | ||||
| import "net/http" | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	validator "gopkg.in/go-playground/validator.v8" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	MIMEJSON              = "application/json" | ||||
| @ -31,6 +35,11 @@ type StructValidator interface { | ||||
| 	// 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 | ||||
|  | ||||
| 	// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key | ||||
| 	// NOTE: if the key already exists, the previous validation function will be replaced. | ||||
| 	// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation | ||||
| 	RegisterValidation(string, validator.Func) error | ||||
| } | ||||
|  | ||||
| var Validator StructValidator = &defaultValidator{} | ||||
|  | ||||
| @ -28,6 +28,11 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (v *defaultValidator) RegisterValidation(key string, fn validator.Func) error { | ||||
| 	v.lazyinit() | ||||
| 	return v.validate.RegisterValidation(key, fn) | ||||
| } | ||||
|  | ||||
| func (v *defaultValidator) lazyinit() { | ||||
| 	v.once.Do(func() { | ||||
| 		config := &validator.Config{TagName: "binding"} | ||||
|  | ||||
| @ -6,9 +6,12 @@ package binding | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	validator "gopkg.in/go-playground/validator.v8" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| @ -190,3 +193,42 @@ func TestValidatePrimitives(t *testing.T) { | ||||
| 	assert.NoError(t, validate(&str)) | ||||
| 	assert.Equal(t, str, "value") | ||||
| } | ||||
|  | ||||
| // structCustomValidation is a helper struct we use to check that | ||||
| // custom validation can be registered on it. | ||||
| // The `notone` binding directive is for custom validation and registered later. | ||||
| type structCustomValidation struct { | ||||
| 	Integer int `binding:"notone"` | ||||
| } | ||||
|  | ||||
| // notOne is a custom validator meant to be used with `validator.v8` library. | ||||
| // The method signature for `v9` is significantly different and this function | ||||
| // would need to be changed for tests to pass after upgrade. | ||||
| // See https://github.com/gin-gonic/gin/pull/1015. | ||||
| func notOne( | ||||
| 	v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, | ||||
| 	field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, | ||||
| ) bool { | ||||
| 	if val, ok := field.Interface().(int); ok { | ||||
| 		return val != 1 | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func TestRegisterValidation(t *testing.T) { | ||||
| 	// This validates that the function `notOne` matches | ||||
| 	// the expected function signature by `defaultValidator` | ||||
| 	// and by extension the validator library. | ||||
| 	err := Validator.RegisterValidation("notone", notOne) | ||||
| 	// Check that we can register custom validation without error | ||||
| 	assert.Nil(t, err) | ||||
|  | ||||
| 	// Create an instance which will fail validation | ||||
| 	withOne := structCustomValidation{Integer: 1} | ||||
| 	errs := validate(withOne) | ||||
|  | ||||
| 	// Check that we got back non-nil errs | ||||
| 	assert.NotNil(t, errs) | ||||
| 	// Check that the error matches expactation | ||||
| 	assert.Error(t, errs, "", "", "notone") | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user