9

I'm new to Go and Gin, and am having trouble printing out the full request body.

I want to be able to read the request body from third party POST, but I'm getting empty request body

curl -u dumbuser:dumbuserpassword -H "Content-Type: application/json" -X POST --data '{"events": "3"}' http://localhost:8080/events

My entire code is as below. Any pointer is appreciated!

package main

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

func main() {
  router := gin.Default()
  authorized := router.Group("/", gin.BasicAuth(gin.Accounts{
     "dumbuser": "dumbuserpassword",
  }))
  authorized.POST("/events", events)
  router.Run(":8080")
}

func events(c *gin.Context) {
  fmt.Printf("%s", c.Request.Body)
  c.JSON(http.StatusOK, c)
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
Leon Hu
  • 137
  • 1
  • 1
  • 5

1 Answers1

32

The problem here is that you're printing out the string value of c.Request.Body, which is of interface type ReadCloser.

What you can do to satisfy yourself that it does in fact contain the body you want is to read the value out of c.Request.Body to a string, and then print that out. This is for your learning process only!

Learning code:

func events(c *gin.Context) {
        x, _ := ioutil.ReadAll(c.Request.Body)
        fmt.Printf("%s", string(x))
        c.JSON(http.StatusOK, c)
}

However, this is not the way you should access the body of the request. Let gin do the parsing of the body for you, by using a binding.

More correct code:

type E struct {
        Events string
}

func events(c *gin.Context) {
        data := &E{}
        c.Bind(data)
        fmt.Println(data)
        c.JSON(http.StatusOK, c)
}

This is a more correct way to access the data in the body, since it will be already parsed out for you. Note that if you read the body first, as we did above in the learning step, the c.Request.Body will have been emptied, and so there will be nothing left in the body for Gin to read.

Broken code:

func events(c *gin.Context) {
    x, _ := ioutil.ReadAll(c.Request.Body)
    fmt.Printf("%s", string(x))
    data := &E{}
    c.Bind(data) // data is left unchanged because c.Request.Body has been used up.
    fmt.Println(data)
    c.JSON(http.StatusOK, c)
}

You're probably also curious why the JSON returned from this endpoint shows and empty Request.Body. This is for the same reason. The JSON Marshalling method cannot serialize a ReadCloser, and so it shows up as empty.

Danver Braganza
  • 1,295
  • 10
  • 10
  • Thank you, Danver, that really helped and was educational. – Leon Hu Aug 13 '15 at 04:36
  • 2
    What is there to do if the binding fails? Since the Reader is emptied, is that data simply lost? – Sean Lindo Oct 13 '16 at 19:53
  • I too would like to know the answer to sean's question. – winduptoy Dec 01 '16 at 01:45
  • Gin does return an error to allow you to detect when a binding fails. However, as far as I can tell, at that point it's too late, and your improperly formatted data is lost. If storing the improper data is a requirement, I think you can try to copy the Req.Body first, but then you will have to perform a call to parse the Json yourself instead of using gin. This is probably worth making a ticket on the gin project. – Danver Braganza Dec 06 '16 at 00:16