Search code examples
gomiddlewarego-gin

Gin If `request body` bound in middleware, c.Request.Body become 0


My API server has middle ware which is getting token from request header. If it access is correct, its go next function.

But request went to middle ware and went to next function, c.Request.Body become 0.

middle ware

func getUserIdFromBody(c *gin.Context) (int) {
    var jsonBody User

    length, _ := strconv.Atoi(c.Request.Header.Get("Content-Length"))
    body := make([]byte, length)
    length, _ = c.Request.Body.Read(body)
    json.Unmarshal(body[:length], &jsonBody)

    return jsonBody.Id
}

func CheckToken() (gin.HandlerFunc) {
    return func(c *gin.Context) {
        var userId int

        config := model.NewConfig()

        reqToken := c.Request.Header.Get("token")

        _, resBool := c.GetQuery("user_id")
        if resBool == false {
            userId = getUserIdFromBody(c)
        } else {
            userIdStr := c.Query("user_id")
            userId, _ = strconv.Atoi(userIdStr)
        }
    ...
        if ok {
            c.Nex()
            return
        }
}

next func

func bindOneDay(c *gin.Context) (model.Oneday, error) {
    var oneday model.Oneday

    if err := c.BindJSON(&oneday); err != nil {
        return oneday, err
    }
    return oneday, nil
}

bindOneDay return error with EOF. because maybe c.Request.Body is 0.

I want to get user_id from request body in middle ware. How to do it without problem that c.Request.Body become 0


Solution

  • You can only read the Body from the client once. The data is streaming from the user, and they're not going to send it again. If you want to read it more than once, you're going to have to buffer the whole thing in memory, like so:

    bodyCopy := new(bytes.Buffer)
    // Read the whole body
    _, err := io.Copy(bodyCopy, req.Body)
    if err != nil {
        return err
    }
    bodyData := bodyCopy.Bytes()
    // Replace the body with a reader that reads from the buffer
    req.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
    // Now you can do something with the contents of bodyData,
    // like passing it to json.Unmarshal
    

    Note that buffering the entire request into memory means that a user can cause you to allocate unlimited memory -- you should probably either block this at a frontend proxy or use an io.LimitedReader to limit the amount of data you'll buffer.

    You also have to read the entire body before Unmarshal can start its work -- this is probably no big deal, but you can do better using io.TeeReader and json.NewDecoder if you're so inclined.

    Better, of course, would be to figure out a way to restructure your code so that buffering the body and decoding it twice aren't necessary.