Ginのリクエストボディはストリーム,,,,,らしい

Ginで受け取れるリクエストボディはストリームなので、一度Readメソッドなどで読み出してしまうと空っぽになってしまう。従って、BindJSONなどのメソッドを読み出した後に使用するとEOFエラーが発生することがある。たとえば以下。

 
package main

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

func main() {
    router := gin.New()

    router.POST("hoge", func(c *gin.Context) {
        buf := make([]byte, 2048)
        n, _ := c.Request.Body.Read(buf) // ここでRequest.Bodyを読み切ってしまっている
        b := string(buf[0:n])
        fmt.Println(b)

        var params RequestParams

        // BindJSONは内部でRequest.Bodyを再び読み出そうとするが
        // Request.Bodyはすでに読み出されて空になっているため、ここでEOFエラーになる
        if err := c.BindJSON(&params); err != nil {
            uc.Warningf("Failed binding request parameters: %s", err.Error())
            c.Status(http.StatusBadRequest)
            return
        }

        c.JSON(200, params)
    })

    router.Run()
}

対策としては、ioutil.NopCloserメソッドを使って読み出してしまったリクエストボディを書き戻すなど。

 
 buf := make([]byte, 2048)
 n, _ := c.Request.Body.Read(buf)
 b := string(buf[0:n])
 c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(s)))
 fmt.Println(b)

stackoverflow.com

 

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

とのことなのでデータはそのまま流れていってしまうそうです