From 5d5b60cf0ae6337e10afcf8c5e58ff577a60108d Mon Sep 17 00:00:00 2001 From: Shintaro Ikeda Date: Fri, 18 Sep 2020 17:22:12 +0900 Subject: [PATCH] Fix ShouldBindBodyWith method --- README.md | 6 +++--- context.go | 5 ++++- context_test.go | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0515e99d..27d1ed6b 100644 --- a/README.md +++ b/README.md @@ -1989,9 +1989,9 @@ func SomeHandler(c *gin.Context) { } ``` -* `c.ShouldBindBodyWith` stores body into the context before binding. This has -a slight impact to performance, so you should not use this method if you are -enough to call binding at once. +* `c.ShouldBindBodyWith` stores body into the context before binding, and it +does not consume the request body. This has a slight impact to performance, +so you should not use this method if you are enough to call binding at once. * This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`, `ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`, can be called by `c.ShouldBind()` multiple times without any damage to diff --git a/context.go b/context.go index 5b60d8b0..09cc1df0 100644 --- a/context.go +++ b/context.go @@ -5,6 +5,7 @@ package gin import ( + "bytes" "errors" "fmt" "io" @@ -706,7 +707,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { // ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request // body into the context, and reuse when it is called again. // -// NOTE: This method reads the body before binding. So you should use +// NOTE: This method copies the body before binding. So you should use // ShouldBindWith for better performance if you need to call only once. func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) { var body []byte @@ -720,6 +721,8 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e if err != nil { return err } + c.Request.Body.Close() + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) c.Set(BodyBytesKey, body) } return bb.BindBody(body, obj) diff --git a/context_test.go b/context_test.go index 8e1e3b57..c5084e11 100644 --- a/context_test.go +++ b/context_test.go @@ -1761,6 +1761,21 @@ func TestContextShouldBindBodyWith(t *testing.T) { assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) assert.Equal(t, typeB{"BAR"}, objB) } + // not affect the request body + { + // After it binds, other bind methods can bind without EOF error. + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.Request, _ = http.NewRequest( + "POST", "http://example.com", bytes.NewBufferString(tt.bodyA), + ) + objA := typeA{} + assert.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) + assert.Equal(t, typeA{"FOO"}, objA) + anotherOjbA := typeA{} + assert.NoError(t, c.ShouldBindWith(&anotherOjbA, tt.bindingA)) + assert.Equal(t, typeA{"FOO"}, anotherOjbA) + } } }