Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use BindJSON multiple times fails #1078

Closed
JorritSalverda opened this issue Aug 24, 2017 · 12 comments
Closed

Use BindJSON multiple times fails #1078

JorritSalverda opened this issue Aug 24, 2017 · 12 comments

Comments

@JorritSalverda
Copy link

When using BindJSON multiple times in a single request handler like below it fails the second time because the c.Request.Body has already been read the first time and can't be read a second time.

var b interface{}
err := c.BindJSON(&b)
...
var requestBody RequestBody
err = c.BindJSON(&requestBody)

I do this to be able to log the full structure of a request body coming into my application so I can refine my RequestBody struct one property at a time. Also when unMarshalling fails I log the origin body as a string, but using the body ReadCloser fails if BindJSON already ran.

A workaround is to go back to the non-gin way of reading the body first

body, err := ioutil.ReadAll(c.Request.Body)
...
var b interface{}
err = json.Unmarshal(body, &b)
...
var requestBody RequestBody
err = json.Unmarshal(body, &requestBody)

But that obviously isn't very nice.

According to https://stackoverflow.com/questions/31884093/read-multiple-time-a-reader using a io.TeeReader in your code would solve the issue. Is that sensible or too slow? Any other options?

JorritSalverda added a commit to estafette/estafette-ci-api that referenced this issue Aug 24, 2017
@JorritSalverda
Copy link
Author

I think this could easily be done by changing line

decoder := json.NewDecoder(req.Body)
to

b := bytes.NewBuffer(make([]byte, 0))
reader := io.TeeReader(req.Body, b)
decoder := json.NewDecoder(reader)

@JorritSalverda
Copy link
Author

Sorry, using TeeReader as above doesn't actually work. Being a golang newbie I misunderstood how it actually works.

@thinkerou
Copy link
Member

If Bind changed the following code:

+var o interface{}
+
 func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
        decoder := json.NewDecoder(req.Body)
        if EnableDecoderUseNumber {
                decoder.UseNumber()
        }
+       if o != nil {
+               return validate(o)
+       }
        if err := decoder.Decode(obj); err != nil {
                return err
        }
+       o = obj
        return validate(obj)
 }

It will solve the issue, but I don't know the method is OK?

@elvizlai
Copy link

@thinkerou I don't think it can work. Async call will obfuscate variable o.

@elvizlai
Copy link

@JorritSalverda I think you can read once then set to ctx, and when u need it, just read it from ctx.

@ptdave20
Copy link

Yes, create a middleware that executes before your request handler. Take your json bytes, and set it in the *gin.Context. When your handler exits, your middlware can continue and you can pull it from the context.

@lishuhao
Copy link

Any update? I have the same problem

@lishuhao
Copy link

I have Found this post ,it may help
Golang: Read from an io.ReadWriter without losing its content

@elvizlai
Copy link

elvizlai commented Apr 16, 2018

@lishuhao yeah, this solved, but maybe it's not the right way. You can't guarantee every middleware using NopCloser to write back. Try to define a []byte in context and reuse it maybe better.

@thinkerou
Copy link
Member

#1341 should fix it.

@thinkerou
Copy link
Member

please use ShouldBindBodyWith

@goldsheva
Copy link

goldsheva commented Feb 11, 2021

import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)

var s1 MyStruct1
var s2 MyStruct2

if err := c.ShouldBindBodyWith(&s1, binding.JSON);..
if err := c.ShouldBindBodyWith(&s2, binding.JSON);..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants