mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-14 20:22:20 +08:00
Bind: return 413 status code when error is http.MaxBytesError
The Go standard library includes a method `http.MaxBytesReader` that allows limiting the request body. For example, users can create a middleware like: ```go func MiddlewareMaxBodySize(c *gin.Context) { // Limit request body to 100 bytes c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 100) c.Next() } ``` When the body exceeds the limit, reading from the request body returns an error of type `http.MaxBytesError`. This PR makes sure that when the error is of kind `http.MaxBytesError`, Gin returns the correct status code 413 (Request Entity Too Large) instead of a generic 400 (Bad Request).
This commit is contained in:
parent
bb3519d26f
commit
bffa5f0eb7
11
context.go
11
context.go
@ -675,8 +675,15 @@ func (c *Context) BindUri(obj any) error {
|
|||||||
// It will abort the request with HTTP 400 if any error occurs.
|
// It will abort the request with HTTP 400 if any error occurs.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) MustBindWith(obj any, b binding.Binding) error {
|
func (c *Context) MustBindWith(obj any, b binding.Binding) error {
|
||||||
if err := c.ShouldBindWith(obj, b); err != nil {
|
err := c.ShouldBindWith(obj, b)
|
||||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
|
if err != nil {
|
||||||
|
maxBytesErr := &http.MaxBytesError{}
|
||||||
|
switch {
|
||||||
|
case errors.As(err, &maxBytesErr):
|
||||||
|
c.AbortWithError(http.StatusRequestEntityTooLarge, err).SetType(ErrorTypeBind) //nolint: errcheck
|
||||||
|
default:
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -425,7 +425,7 @@ func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
|
|||||||
|
|
||||||
func TestContextQueryAndPostForm(t *testing.T) {
|
func TestContextQueryAndPostForm(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
|
body := strings.NewReader("foo=bar&page=11&both=&foo=second")
|
||||||
c.Request, _ = http.NewRequest("POST",
|
c.Request, _ = http.NewRequest("POST",
|
||||||
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
|
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
|
||||||
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
||||||
@ -700,7 +700,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) {
|
|||||||
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +726,7 @@ func TestContextRenderAPIJSON(t *testing.T) {
|
|||||||
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1186,7 +1186,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1602,9 +1602,29 @@ func TestContextContentType(t *testing.T) {
|
|||||||
assert.Equal(t, "application/json", c.ContentType())
|
assert.Equal(t, "application/json", c.ContentType())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextBindRequestTooLarge(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
|
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10)
|
||||||
|
|
||||||
|
var obj struct {
|
||||||
|
Foo string `json:"foo"`
|
||||||
|
Bar string `json:"bar"`
|
||||||
|
}
|
||||||
|
assert.Error(t, c.BindJSON(&obj))
|
||||||
|
c.Writer.WriteHeaderNow()
|
||||||
|
|
||||||
|
assert.Empty(t, obj.Bar)
|
||||||
|
assert.Empty(t, obj.Foo)
|
||||||
|
assert.Equal(t, http.StatusRequestEntityTooLarge, w.Code)
|
||||||
|
assert.True(t, c.IsAborted())
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextAutoBindJSON(t *testing.T) {
|
func TestContextAutoBindJSON(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1621,7 +1641,7 @@ func TestContextBindWithJSON(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1638,7 +1658,7 @@ func TestContextBindWithXML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<root>
|
<root>
|
||||||
<foo>FOO</foo>
|
<foo>FOO</foo>
|
||||||
<bar>BAR</bar>
|
<bar>BAR</bar>
|
||||||
@ -1681,7 +1701,7 @@ func TestContextBindWithQuery(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
|
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", strings.NewReader("foo=unused"))
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `form:"foo"`
|
Foo string `form:"foo"`
|
||||||
@ -1697,7 +1717,7 @@ func TestContextBindWithYAML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader("foo: bar\nbar: foo"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1714,7 +1734,7 @@ func TestContextBindWithTOML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader("foo = 'bar'\nbar = 'foo'"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1731,7 +1751,7 @@ func TestContextBadAutoBind(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "http://example.com", strings.NewReader("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `json:"foo"`
|
Foo string `json:"foo"`
|
||||||
@ -1750,7 +1770,7 @@ func TestContextBadAutoBind(t *testing.T) {
|
|||||||
|
|
||||||
func TestContextAutoShouldBindJSON(t *testing.T) {
|
func TestContextAutoShouldBindJSON(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1767,7 +1787,7 @@ func TestContextShouldBindWithJSON(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1784,7 +1804,7 @@ func TestContextShouldBindWithXML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<root>
|
<root>
|
||||||
<foo>FOO</foo>
|
<foo>FOO</foo>
|
||||||
<bar>BAR</bar>
|
<bar>BAR</bar>
|
||||||
@ -1827,7 +1847,7 @@ func TestContextShouldBindWithQuery(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
|
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", strings.NewReader("foo=unused"))
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `form:"foo"`
|
Foo string `form:"foo"`
|
||||||
@ -1847,7 +1867,7 @@ func TestContextShouldBindWithYAML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader("foo: bar\nbar: foo"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1864,7 +1884,7 @@ func TestContextShouldBindWithTOML(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader("foo='bar'\nbar= 'foo'"))
|
||||||
c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
|
c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
|
||||||
|
|
||||||
var obj struct {
|
var obj struct {
|
||||||
@ -1881,7 +1901,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "http://example.com", strings.NewReader("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `json:"foo"`
|
Foo string `json:"foo"`
|
||||||
@ -1945,7 +1965,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest(
|
c.Request, _ = http.NewRequest(
|
||||||
"POST", "http://example.com", bytes.NewBufferString(tt.bodyA),
|
"POST", "http://example.com", strings.NewReader(tt.bodyA),
|
||||||
)
|
)
|
||||||
// When it binds to typeA and typeB, it finds the body is
|
// When it binds to typeA and typeB, it finds the body is
|
||||||
// not typeB but typeA.
|
// not typeB but typeA.
|
||||||
@ -1963,7 +1983,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest(
|
c.Request, _ = http.NewRequest(
|
||||||
"POST", "http://example.com", bytes.NewBufferString(tt.bodyB),
|
"POST", "http://example.com", strings.NewReader(tt.bodyB),
|
||||||
)
|
)
|
||||||
objA := typeA{}
|
objA := typeA{}
|
||||||
assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
||||||
@ -1977,7 +1997,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
|
|||||||
|
|
||||||
func TestContextGolangContext(t *testing.T) {
|
func TestContextGolangContext(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", strings.NewReader(`{"foo":"bar", "bar":"foo"}`))
|
||||||
assert.NoError(t, c.Err())
|
assert.NoError(t, c.Err())
|
||||||
assert.Nil(t, c.Done())
|
assert.Nil(t, c.Done())
|
||||||
ti, ok := c.Deadline()
|
ti, ok := c.Deadline()
|
||||||
@ -2025,7 +2045,7 @@ func TestGetRequestHeaderValue(t *testing.T) {
|
|||||||
|
|
||||||
func TestContextGetRawData(t *testing.T) {
|
func TestContextGetRawData(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
body := bytes.NewBufferString("Fetch binary post data")
|
body := strings.NewReader("Fetch binary post data")
|
||||||
c.Request, _ = http.NewRequest("POST", "/", body)
|
c.Request, _ = http.NewRequest("POST", "/", body)
|
||||||
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user