1

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

oguz ismail
  • 39,105
  • 12
  • 41
  • 62
rluisr
  • 333
  • 5
  • 15
  • I don't understand what "c.Request.Body is 0" means. c.Request.Body is an io.ReadCloser--`0` is an impossible value for an io.ReadCloser. Can you elaborate on what you mean? Do you mean that the value read from the body is the string `"0"`? Or do you mean that it's 0-bytes long? Or something else? – Flimzy Dec 27 '17 at 10:58
  • @Flimzy sorry `c.Request.Body.Read` not `c.Request.Body` – rluisr Dec 27 '17 at 12:02
  • `c.Request.Body.Read` is a function, not an integer. Are you saying _that_ returns `0` as the number of bytes read? – Flimzy Dec 27 '17 at 12:04
  • @Flimzy yes `fmt.Println(c.Request.Body.Read(body))` – rluisr Dec 27 '17 at 12:41

1 Answers1

8

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.

hobbs
  • 206,796
  • 16
  • 199
  • 282
  • 1
    Thanks @hobbs ! solve this problem. I learned a lot from your comment! – rluisr Dec 27 '17 at 12:09
  • If you'd like please teach me how to use `io.LimitedReader`. I don't understand even googled this method. – rluisr Dec 27 '17 at 12:22
  • @rluisr this might help [Limiting amount of data read in the response to a HTTP GET request](https://stackoverflow.com/a/38874756/678491) – k1m190r Dec 27 '17 at 14:22
  • @biosckon thanks comment. this code is return `0`. Do you know what is wrong? https://play.golang.org/p/40c6XQ44ESz – rluisr Dec 28 '17 at 09:28
  • @rluisr if you have solved it, please post answer to your own question. – k1m190r Dec 28 '17 at 11:23