1
0
mirror of https://github.com/labstack/echo.git synced 2026-05-16 09:48:24 +02:00

Revert Issue #2813 fix based on maintainer feedback

Revert the DefaultBinder empty body handling changes following
@aldas's concerns about:
- Body replacement potentially interfering with custom readers
- Lack of proper reproduction case for the original issue
- Potential over-engineering for an edge case

The "read one byte and reconstruct body" approach could interfere
with users who add custom readers with specific behavior.

Waiting for better reproduction case and less invasive solution.

Refs: https://github.com/labstack/echo/issues/2813#issuecomment-3294563361

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vishal Rana
2025-09-15 19:08:27 -07:00
parent 2fb84197e9
commit d0137c3e80
2 changed files with 139 additions and 28 deletions
+113 -8
View File
@@ -1082,14 +1082,6 @@ func TestDefaultBinder_BindBody(t *testing.T) {
givenContent: strings.NewReader(""),
expect: &Node{ID: 0, Node: ""},
},
{
name: "ok, POST with empty body and ContentLength -1 (Issue #2813)",
givenURL: "/api/real_node/endpoint?node=xxx",
givenMethod: http.MethodPost,
givenContent: strings.NewReader(""),
whenChunkedBody: true, // This sets ContentLength to -1
expect: &Node{ID: 0, Node: ""},
},
{
name: "ok, JSON POST bind to struct with: path + query + chunked body",
givenURL: "/api/real_node/endpoint?node=xxx",
@@ -1579,3 +1571,116 @@ func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file test
err = fl.Close()
assert.NoError(t, err)
}
func TestTimeFormatBinding(t *testing.T) {
type TestStruct struct {
DateTimeLocal time.Time `form:"datetime_local" format:"2006-01-02T15:04"`
Date time.Time `query:"date" format:"2006-01-02"`
CustomFormat time.Time `form:"custom" format:"01/02/2006 15:04:05"`
DefaultTime time.Time `form:"default_time"` // No format tag - should use default parsing
PtrTime *time.Time `query:"ptr_time" format:"2006-01-02"`
}
testCases := []struct {
name string
contentType string
data string
queryParams string
expect TestStruct
expectError bool
}{
{
name: "ok, datetime-local format binding",
contentType: MIMEApplicationForm,
data: "datetime_local=2023-12-25T14:30&default_time=2023-12-25T14:30:45Z",
expect: TestStruct{
DateTimeLocal: time.Date(2023, 12, 25, 14, 30, 0, 0, time.UTC),
DefaultTime: time.Date(2023, 12, 25, 14, 30, 45, 0, time.UTC),
},
},
{
name: "ok, date format binding via query params",
queryParams: "?date=2023-01-15&ptr_time=2023-02-20",
expect: TestStruct{
Date: time.Date(2023, 1, 15, 0, 0, 0, 0, time.UTC),
PtrTime: &time.Time{},
},
},
{
name: "ok, custom format via form data",
contentType: MIMEApplicationForm,
data: "custom=12/25/2023 14:30:45",
expect: TestStruct{
CustomFormat: time.Date(2023, 12, 25, 14, 30, 45, 0, time.UTC),
},
},
{
name: "nok, invalid format should fail",
contentType: MIMEApplicationForm,
data: "datetime_local=invalid-date",
expectError: true,
},
{
name: "nok, wrong format should fail",
contentType: MIMEApplicationForm,
data: "datetime_local=2023-12-25", // Missing time part
expectError: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
e := New()
var req *http.Request
if tc.contentType == MIMEApplicationJSON {
req = httptest.NewRequest(http.MethodPost, "/"+tc.queryParams, strings.NewReader(tc.data))
req.Header.Set(HeaderContentType, tc.contentType)
} else if tc.contentType == MIMEApplicationForm {
req = httptest.NewRequest(http.MethodPost, "/"+tc.queryParams, strings.NewReader(tc.data))
req.Header.Set(HeaderContentType, tc.contentType)
} else {
req = httptest.NewRequest(http.MethodGet, "/"+tc.queryParams, nil)
}
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
var result TestStruct
err := c.Bind(&result)
if tc.expectError {
assert.Error(t, err)
return
}
assert.NoError(t, err)
// Check individual fields since time comparison can be tricky
if !tc.expect.DateTimeLocal.IsZero() {
assert.True(t, tc.expect.DateTimeLocal.Equal(result.DateTimeLocal),
"DateTimeLocal: expected %v, got %v", tc.expect.DateTimeLocal, result.DateTimeLocal)
}
if !tc.expect.Date.IsZero() {
assert.True(t, tc.expect.Date.Equal(result.Date),
"Date: expected %v, got %v", tc.expect.Date, result.Date)
}
if !tc.expect.CustomFormat.IsZero() {
assert.True(t, tc.expect.CustomFormat.Equal(result.CustomFormat),
"CustomFormat: expected %v, got %v", tc.expect.CustomFormat, result.CustomFormat)
}
if !tc.expect.DefaultTime.IsZero() {
assert.True(t, tc.expect.DefaultTime.Equal(result.DefaultTime),
"DefaultTime: expected %v, got %v", tc.expect.DefaultTime, result.DefaultTime)
}
if tc.expect.PtrTime != nil {
assert.NotNil(t, result.PtrTime)
if result.PtrTime != nil {
expectedPtr := time.Date(2023, 2, 20, 0, 0, 0, 0, time.UTC)
assert.True(t, expectedPtr.Equal(*result.PtrTime),
"PtrTime: expected %v, got %v", expectedPtr, *result.PtrTime)
}
}
})
}
}