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?
I think this could easily be done by changing line https://github.com/gin-gonic/gin/blob/ce670a64977a513d8caa58c143471ddbbf641bd4/binding/json.go#L24 to
b := bytes.NewBuffer(make([]byte, 0))
reader := io.TeeReader(req.Body, b)
decoder := json.NewDecoder(reader)
Sorry, using TeeReader as above doesn't actually work. Being a golang newbie I misunderstood how it actually works.
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?
@thinkerou I don't think it can work. Async call will obfuscate variable o.
@JorritSalverda I think you can read once then set to ctx, and when u need it, just read it from ctx.
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.
Any update? I have the same problem
I have Found this post ,it may help
Golang: Read from an io.ReadWriter without losing its content
@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.
https://github.com/gin-gonic/gin/pull/1341 should fix it.
please use ShouldBindBodyWith
Most helpful comment
I have Found this post ,it may help
Golang: Read from an io.ReadWriter without losing its content