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:
Suhas Karanth
2017-08-27 13:07:39 +05:30
committed by Javier Provecho Fernandez
parent 030b1aaf72
commit 26c3f42095
5 changed files with 175 additions and 13 deletions

View File

@ -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{}

View File

@ -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"}

View File

@ -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")
}