diff --git a/bind.go b/bind.go index 0e5ddd469..ce383143a 100644 --- a/bind.go +++ b/bind.go @@ -216,12 +216,19 @@ func bindMetaFor(typ reflect.Type) *bindStructMeta { // bindData will bind data ONLY fields in destination struct that have EXPLICIT tag func bindData(destination any, data map[string][]string, tag string, dataFiles map[string][]*multipart.FileHeader) error { - if destination == nil || (len(data) == 0 && len(dataFiles) == 0) { + if destination == nil { return nil } hasFiles := len(dataFiles) > 0 - typ := reflect.TypeOf(destination).Elem() - val := reflect.ValueOf(destination).Elem() + rv := reflect.ValueOf(destination) + if rv.Kind() != reflect.Pointer || rv.IsNil() { + return errors.New("binding destination must be a non-nil pointer") + } + if len(data) == 0 && len(dataFiles) == 0 { + return nil + } + typ := rv.Type().Elem() + val := rv.Elem() // Support binding to limited Map destinations: // - map[string][]string, diff --git a/bind_test.go b/bind_test.go index c1ef54435..0cfb62157 100644 --- a/bind_test.go +++ b/bind_test.go @@ -249,6 +249,28 @@ func TestBindQueryParams(t *testing.T) { } } +func TestBindQueryParamsToNonPointer(t *testing.T) { + e := New() + req := httptest.NewRequest(http.MethodGet, "/?id=1", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + // Binding into a value instead of a pointer must return an error and must + // not panic. + var err error + assert.NotPanics(t, func() { + err = c.Bind(user{}) + }) + assert.ErrorContains(t, err, "binding destination must be a non-nil pointer") + + // The error is reported as a bad request, the same way the JSON binder + // reports a destination that is not a pointer. + var httpErr *HTTPError + if assert.ErrorAs(t, err, &httpErr) { + assert.Equal(t, http.StatusBadRequest, httpErr.Code) + } +} + func TestBindQueryParamsCaseInsensitive(t *testing.T) { e := New() req := httptest.NewRequest(http.MethodGet, "/?ID=1&NAME=Jon+Snow", nil)