Description

Calling c.GetRawData() in a middleware means you cannot later bind on your request body.

One reason to call GetRawData in a middleware is to verify a body signature. For example, a webhook may be sent and signed with a key to indicate the webhook sender is valid. Implementing the webhook receiver means you want to verify the signatures match before taking any action.

How to reproduce

package main

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

func main() {
    g := gin.Default()

    g.Use(func(c *gin.Context) {
        c.GetRawData()

    })

    g.POST("/hello", func(c *gin.Context) {
        type helloBody struct {
            Name string `json:"name"`
        }
        var body helloBody
        c.BindJSON(&body)
        c.String(200, body.Name)

    })
    g.Run(":9000")
}

Expectations

We should get a 200 and the name be echoed back

curl -X POST localhost:9000/hello -d '{"name": "john"}
john

Actual result

curl -v  -X POST localhost:9000/hello -d '{"name": "ted"}'
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:9000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /hello HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 15
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 15 out of 15 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Date: Sat, 02 Mar 2024 05:19:05 GMT
< Content-Length: 0

Environment

  • go version: 1.22
  • gin version (or commit ref): 1.9.1
  • operating system: linux

Comment From: dbhoot

My way forward is to call c.ShouldBindBodyWith(...) in my middleware and then c.Get(gin.BodyBytesKey) to get the body. Then later in the actual handler function, I use c.ShouldBindBodyWith(...) again to actually use the body.

Comment From: RedCrazyGhost

According to what you said, I processed the code, you can take a look at my code below:

package main

import (
    "errors"
    "net/http"

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

func main() {
    g := gin.Default()

    g.Use(func(c *gin.Context) {
        body, err := c.GetRawData()
        if err != nil {
            c.Abort()
            c.JSON(http.StatusInternalServerError,errors.New("body handling exception"))
        } else {
            c.Set(gin.BodyBytesKey,body)
        }
    })

    g.POST("/hello", func(c *gin.Context) {
        type helloBody struct {
            Name string `json:"name"`
        }
        var body helloBody

        err := c.ShouldBindBodyWith(&body,binding.JSON)
        if err != nil {
            return 
        }

        c.String(200, body.Name)

    })
    g.Run(":9000")
}

Storing the Body into context negatively affects processing in highly concurrent scenarios and requires you to manually handle the Content-Type yourself.

NOTE: This method reads the body before binding. So you should use ShouldBindWith for better performance if you need to call only once.

It is recommended to use the official default BodyBytesKey, and it is also possible to store your own key in context.

// BodyBytesKey indicates a default body bytes key.
const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"

Comment From: dbhoot

Thanks. I read through the source and that is indeed how I solved the problem.