mirror of
https://github.com/gin-gonic/gin.git
synced 2026-04-29 23:23:18 +08:00
feat: add warning for multiple response writes
When the response body is written multiple times in a single request, Gin now prints a warning in debug mode. This helps developers identify a common mistake where middleware or handlers accidentally write responses more than once, resulting in invalid concatenated output. The warning is printed via debugPrint() and only appears in debug mode, making this change non-breaking and backwards compatible. Fixes #4477 Example warning output: [GIN-debug] [WARNING] Response body already written. Attempting to write again with status code 200 Changes: - Added check in Context.Render() to detect multiple writes - Added unit tests to verify warning behavior in debug mode - Added test to verify no warning in release mode
This commit is contained in:
parent
d3ffc99852
commit
ec2c838995
@ -1158,6 +1158,10 @@ func (c *Context) Render(code int, r render.Render) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Writer.Written() {
|
||||||
|
debugPrint("[WARNING] Response body already written. Attempting to write again with status code %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.Render(c.Writer); err != nil {
|
if err := r.Render(c.Writer); err != nil {
|
||||||
// Pushing error to c.Errors
|
// Pushing error to c.Errors
|
||||||
_ = c.Error(err)
|
_ = c.Error(err)
|
||||||
|
|||||||
@ -3808,3 +3808,47 @@ func BenchmarkGetMapFromFormData(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderMultipleWritesWarning(t *testing.T) {
|
||||||
|
// Test that a warning is printed when attempting to write response body multiple times
|
||||||
|
var w *httptest.ResponseRecorder
|
||||||
|
re := captureOutput(t, func() {
|
||||||
|
SetMode(DebugMode)
|
||||||
|
router := New()
|
||||||
|
router.GET("/test", func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"first": "response"})
|
||||||
|
c.JSON(http.StatusOK, H{"second": "response"}) // Should trigger warning
|
||||||
|
})
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
SetMode(TestMode)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Should contain the warning about multiple writes
|
||||||
|
assert.Contains(t, re, "[WARNING] Response body already written")
|
||||||
|
assert.Contains(t, re, "200")
|
||||||
|
|
||||||
|
// Verify the response body contains both responses (behavior unchanged)
|
||||||
|
assert.Contains(t, w.Body.String(), "first")
|
||||||
|
assert.Contains(t, w.Body.String(), "second")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderMultipleWritesNoWarningInReleaseMode(t *testing.T) {
|
||||||
|
// Test that no warning is printed in release mode
|
||||||
|
re := captureOutput(t, func() {
|
||||||
|
SetMode(ReleaseMode)
|
||||||
|
router := New()
|
||||||
|
router.GET("/test", func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"first": "response"})
|
||||||
|
c.JSON(http.StatusOK, H{"second": "response"})
|
||||||
|
})
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
SetMode(TestMode)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Should not contain the warning in release mode
|
||||||
|
assert.NotContains(t, re, "[WARNING] Response body already written")
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user