diff --git a/context.go b/context.go index 046f284e..3d7927c7 100644 --- a/context.go +++ b/context.go @@ -5,6 +5,7 @@ package gin import ( + "bytes" "errors" "fmt" "io" @@ -33,7 +34,6 @@ const ( MIMEPOSTForm = binding.MIMEPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEYAML = binding.MIMEYAML - BodyBytesKey = "_gin-gonic/gin/bodybyteskey" ) const abortIndex int8 = math.MaxInt8 / 2 @@ -666,19 +666,15 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { // NOTE: This method reads 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 - if cb, ok := c.Get(BodyBytesKey); ok { - if cbb, ok := cb.([]byte); ok { - body = cbb - } - } - if body == nil { - body, err = ioutil.ReadAll(c.Request.Body) - if err != nil { - return err - } - c.Set(BodyBytesKey, body) + var buf bytes.Buffer + tee := io.TeeReader(c.Request.Body, &buf) + body, err := ioutil.ReadAll(tee) + if err != nil { + return err } + defer c.Request.Body.Close() + + c.Request.Body = ioutil.NopCloser(&buf) return bb.BindBody(body, obj) } diff --git a/context_test.go b/context_test.go index f7bb0f51..de7d39c4 100644 --- a/context_test.go +++ b/context_test.go @@ -1651,31 +1651,38 @@ func TestContextShouldBindBodyWith(t *testing.T) { Bar string `json:"bar" xml:"bar" binding:"required"` } for _, tt := range []struct { - name string - bindingA, bindingB binding.BindingBody - bodyA, bodyB string + name string + bindingA, bindingB binding.BindingBody + bodyA, bodyB string + contentTypeA, contentTypeB string }{ { - name: "JSON & JSON", - bindingA: binding.JSON, - bindingB: binding.JSON, - bodyA: `{"foo":"FOO"}`, - bodyB: `{"bar":"BAR"}`, + name: "JSON & JSON", + bindingA: binding.JSON, + bindingB: binding.JSON, + bodyA: `{"foo":"FOO"}`, + bodyB: `{"bar":"BAR"}`, + contentTypeA: binding.MIMEJSON, + contentTypeB: binding.MIMEJSON, }, { - name: "JSON & XML", - bindingA: binding.JSON, - bindingB: binding.XML, - bodyA: `{"foo":"FOO"}`, + name: "JSON & XML", + bindingA: binding.JSON, + bindingB: binding.XML, + contentTypeA: binding.MIMEJSON, + contentTypeB: binding.MIMEXML, + bodyA: `{"foo":"FOO"}`, bodyB: ` BAR `, }, { - name: "XML & XML", - bindingA: binding.XML, - bindingB: binding.XML, + name: "XML & XML", + bindingA: binding.XML, + bindingB: binding.XML, + contentTypeA: binding.MIMEXML, + contentTypeB: binding.MIMEXML, bodyA: ` FOO @@ -1694,6 +1701,7 @@ func TestContextShouldBindBodyWith(t *testing.T) { c.Request, _ = http.NewRequest( "POST", "http://example.com", bytes.NewBufferString(tt.bodyA), ) + c.Request.Header.Add("Content-Type", tt.contentTypeA) // When it binds to typeA and typeB, it finds the body is // not typeB but typeA. objA := typeA{} @@ -1702,6 +1710,9 @@ func TestContextShouldBindBodyWith(t *testing.T) { objB := typeB{} assert.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) assert.NotEqual(t, typeB{"BAR"}, objB) + objB2 := typeB{} + assert.Error(t, c.ShouldBind(&objB2)) + assert.NotEqual(t, typeB{"BAR"}, objB2) } // bodyB to typeA and typeB { @@ -1712,12 +1723,16 @@ func TestContextShouldBindBodyWith(t *testing.T) { c.Request, _ = http.NewRequest( "POST", "http://example.com", bytes.NewBufferString(tt.bodyB), ) + c.Request.Header.Add("Content-Type", tt.contentTypeB) objA := typeA{} assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) assert.NotEqual(t, typeA{"FOO"}, objA) objB := typeB{} assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB)) assert.Equal(t, typeB{"BAR"}, objB) + objB2 := typeB{} + assert.NoError(t, c.ShouldBind(&objB2)) + assert.Equal(t, typeB{"BAR"}, objB2) } } }