diff --git a/pkg/web/binding.go b/pkg/web/binding.go index 6bd47cc3994..94862b30621 100644 --- a/pkg/web/binding.go +++ b/pkg/web/binding.go @@ -26,14 +26,27 @@ type Validator interface { } func validate(obj interface{}) error { + // First check if obj is nil, because we cannot validate those. + if obj == nil { + return nil + } + + // Second, check if obj has a nil interface value. + // This is to prevent panics when obj is an instance of uninitialised struct pointer / interface. + t := reflect.TypeOf(obj) + v := reflect.ValueOf(obj) + + if v.Kind() == reflect.Ptr && v.IsNil() { + return nil + } + // If type has a Validate() method - use that if validator, ok := obj.(Validator); ok { return validator.Validate() } + // Otherwise, use reflection to match `binding:"Required"` struct field tags. // Resolve all pointers and interfaces, until we get a concrete type. - t := reflect.TypeOf(obj) - v := reflect.ValueOf(obj) for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr { t = t.Elem() v = v.Elem() diff --git a/pkg/web/binding_test.go b/pkg/web/binding_test.go index c0ccbd13f50..38d983bd0e2 100644 --- a/pkg/web/binding_test.go +++ b/pkg/web/binding_test.go @@ -50,8 +50,22 @@ func (sv StructWithValidation) Validate() error { return nil } +type StructWithPointerValidation struct { + A int +} + +func (sv *StructWithPointerValidation) Validate() error { + if sv.A < 10 { + return errors.New("too small") + } + return nil +} + func TestValidationSuccess(t *testing.T) { + var nilInterface *StructWithPointerValidation + for _, x := range []interface{}{ + nil, 42, "foo", struct{ A int }{}, @@ -65,6 +79,8 @@ func TestValidationSuccess(t *testing.T) { StructWithStruct{StructWithInt{3}}, StructWithStructPointer{&StructWithInt{3}}, StructWithValidation{42}, + &StructWithPointerValidation{42}, + nilInterface, } { if err := validate(x); err != nil { t.Error("Validation failed:", x, err) @@ -93,6 +109,7 @@ func TestValidationFailure(t *testing.T) { StructWithStructPointer{}, StructWithStructPointer{&StructWithInt{}}, StructWithValidation{2}, + &StructWithPointerValidation{2}, } { if err := validate(x); err == nil { t.Error("Validation should fail:", i, x)