gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
76.72k stars 7.91k forks source link

Request body disappear if we access it on middleware #1651

Closed Clivern closed 5 years ago

Clivern commented 5 years ago

Description

Request body disappear on controller if we access it on middleware or two times on general. but this can be fixed if we read the body and then reassign the body again on GetRawData method.

Code

// Copyright 2018 Clivern. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

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

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        rawBody, _ := c.GetRawData()
        log.Println("first", string(rawBody))
    }
}

func main() {
    r := gin.New()
    r.Use(Logger())

    r.POST("/test", func(c *gin.Context) {
        // Body disappear on controller
        NrawBody, _ := c.GetRawData()
        log.Println("second", string(NrawBody))
    })

    // Listen and serve on 0.0.0.0:8080
    r.Run(":8080")
}

[GIN-debug] POST /test --> main.main.func1 (2 handlers) [GIN-debug] Listening and serving HTTP on :8080 2018/11/19 10:50:31 first {"key":"app_name","value":"Beaver"} 2018/11/19 10:50:31 second


## Working Code
```go
// Copyright 2018 Clivern. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
    "log"
    "io/ioutil"
    "bytes"
    "github.com/gin-gonic/gin"
)

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        var bodyBytes []byte
        if c.Request.Body != nil {
          bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
        }
       c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

        log.Println("first", string(bodyBytes))
    }
}

func main() {
    r := gin.New()
    r.Use(Logger())

    r.POST("/test", func(c *gin.Context) {
        // Body disappear on controller
        NrawBody, _ := c.GetRawData()
        log.Println("second", string(NrawBody))
    })

    // Listen and serve on 0.0.0.0:8080
    r.Run(":8080")
}

[GIN-debug] POST /test --> main.main.func1 (2 handlers) [GIN-debug] Listening and serving HTTP on :8080 2018/11/19 11:17:41 first {"key":"app_name","value":"Beaver"} 2018/11/19 11:17:41 second {"key":"app_name","value":"Beaver"}



**I am not sure if gin community would like to support that?**
thinkerou commented 5 years ago

c.GetRawData() only call one time, it has nothing to do with middleware

Clivern commented 5 years ago

@thinkerou i was thinking it is a good idea to make it accessible multiple times and it can be done BTW. Middlewares most of times used as logging layers to incoming requests but anyway there is an available approach so this issue can be closed.

duktig-dev commented 1 year ago

@Clivern Would be nice id you share the available approach. I'm trying to get Raw request data after c.ShouldBindJSON(...), but as mentioned in documentation, after the binding, the body is not available.

Thanks !

Clivern commented 1 year ago

@duktig-dev I've shared the working example above. So you can store the request body on a local variable then use c.ShouldBindJSON(...) then assign it back ... etc

import (
    "io/ioutil"
    "bytes"
)

r.POST("/test", func(c *gin.Context) {
    var bodyBytes []byte
    if c.Request.Body != nil {
      bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
    }

    // Access Request Body
    c.ShouldBindJSON(...)

    // Assign Back the request body
    c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

    // Access Request Body Again
    c.GetRawData()
})
duktig-dev commented 1 year ago

@Clivern Thank you!