From 4a81d5807a59d026cc55998c23460cd385c56b44 Mon Sep 17 00:00:00 2001 From: sh9336 Date: Fri, 9 Jan 2026 17:27:06 +0530 Subject: [PATCH 1/2] Wrap EOF with helpful message for empty JSON request body --- binding/json.go | 5 +++++ binding/json_external_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 binding/json_external_test.go diff --git a/binding/json.go b/binding/json.go index f4ae921a..8d524a9d 100644 --- a/binding/json.go +++ b/binding/json.go @@ -7,6 +7,7 @@ package binding import ( "bytes" "errors" + "fmt" "io" "net/http" @@ -50,7 +51,11 @@ func decodeJSON(r io.Reader, obj any) error { decoder.DisallowUnknownFields() } if err := decoder.Decode(obj); err != nil { + if err == io.EOF { + return fmt.Errorf("empty request body: %w", err) + } return err } + return validate(obj) } diff --git a/binding/json_external_test.go b/binding/json_external_test.go new file mode 100644 index 00000000..32660178 --- /dev/null +++ b/binding/json_external_test.go @@ -0,0 +1,38 @@ +package binding_test + +import ( + "bytes" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestJSONBindingEmptyBodyReturnsHelpfulError(t *testing.T) { + type Req struct { + Name string `json:"name" binding:"required"` + } + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + + req, err := http.NewRequest(http.MethodPost, "/", bytes.NewBuffer(nil)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + c.Request = req + + var r Req + err = c.ShouldBindJSON(&r) + + assert.Error(t, err) + + // Current behavior returns plain "EOF", which is not helpful. + assert.NotEqual(t, "EOF", err.Error(), "error message should not be plain EOF") + assert.Contains(t, err.Error(), "empty request body") + assert.ErrorIs(t, err, io.EOF) + +} From cd9b91bb2ef67d3b409e8c2465c806a2d68f5178 Mon Sep 17 00:00:00 2001 From: sh9336 Date: Sat, 10 Jan 2026 10:26:46 +0530 Subject: [PATCH 2/2] Fix lint issues: error wrapping, require assertions, formatting --- binding/json.go | 3 ++- binding/json_external_test.go | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/binding/json.go b/binding/json.go index 8d524a9d..047b9910 100644 --- a/binding/json.go +++ b/binding/json.go @@ -51,9 +51,10 @@ func decodeJSON(r io.Reader, obj any) error { decoder.DisallowUnknownFields() } if err := decoder.Decode(obj); err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { return fmt.Errorf("empty request body: %w", err) } + return err } diff --git a/binding/json_external_test.go b/binding/json_external_test.go index 32660178..dee7b0d0 100644 --- a/binding/json_external_test.go +++ b/binding/json_external_test.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestJSONBindingEmptyBodyReturnsHelpfulError(t *testing.T) { @@ -20,7 +21,7 @@ func TestJSONBindingEmptyBodyReturnsHelpfulError(t *testing.T) { c, _ := gin.CreateTestContext(w) req, err := http.NewRequest(http.MethodPost, "/", bytes.NewBuffer(nil)) - assert.NoError(t, err) + require.NoError(t, err) req.Header.Set("Content-Type", "application/json") c.Request = req @@ -28,11 +29,11 @@ func TestJSONBindingEmptyBodyReturnsHelpfulError(t *testing.T) { var r Req err = c.ShouldBindJSON(&r) - assert.Error(t, err) + require.Error(t, err) - // Current behavior returns plain "EOF", which is not helpful. - assert.NotEqual(t, "EOF", err.Error(), "error message should not be plain EOF") + // Error message should be more descriptive than plain EOF, + // while still preserving io.EOF via wrapping. + assert.NotEqual(t, "EOF", err.Error()) assert.Contains(t, err.Error(), "empty request body") assert.ErrorIs(t, err, io.EOF) - }