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:
+113
-8
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user