How to log or print request received with gin? - go

Example:
func createOrUpdateInfluencer(c *gin.Context) { }
How to print the data in the request received in my function?
In my case, I am supposed to receive JSON, how to print it without knowing what it looks like?

Just read and print the body is ok:
func createOrUpdateInfluencer(c *gin.Context) {
body, _ := ioutil.ReadAll(c.Request.Body)
println(string(body))
}
Or if you just want to peek it in middleware, you can put it back after read:
func createOrUpdateInfluencer(c *gin.Context) {
body, _ := ioutil.ReadAll(c.Request.Body)
println(string(body))
c.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
}

by using c.Request you can access to your reqeust object, then print/log everyting you must, like the headers:
fmt.Println(g.Request.Header)
fmt.Println(g.Request.Host)
// etc

Related

How to log response body in gin while I redirect the initial request

I'm trying to log the response body of a request that has been redirected.
func main() {
r := gin.Default()
eanAPI := api.NewEanAPI()
v1 := r.Group("/v1")
v1.POST("/*action", eanAPI.Redirect, middleware.SaveRequest())
port := os.Getenv("PORT")
if len(port) == 0 {
port = "8000"
}
r.Run(":" + port)
}
func (api *eanAPI) Redirect(ctx *gin.Context) {
forwardToHost := "https://jsonplaceholder.typicode.com"
url := ctx.Request.URL.String()
ctx.Redirect(http.StatusTemporaryRedirect, forwardToHost)
}
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func SaveRequest() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
statusCode := c.Writer.Status()
fmt.Println("status: ", statusCode)
if statusCode <= 400 {
//ok this is an request with error, let's make a record for it
// now print body (or log in your preferred way)
fmt.Println("Response body: " + blw.body.String())
}
}
Unfortunately, the body response is always empty. Maybe the redirection or the middleware is messing with my body response
When I tried with postman I can see the body response coming! You can try it by a POST on https://jsonplaceholder.typicode.com/posts. It should return a payload with an id in the body response
What am I doing wrong here?
Thanks in advance
The response body you're seeing in the browser/postman is from the page you're being redirected to. The page doing the actual redirecting likely has no response body, just a status and a Location header. This is the expected behavior of a redirect. If you want to try to capture the body of the page you're redirecting to, you can't actually use redirects for that (the redirect handler isn't involved in the final request); you'd have to fully proxy the request rather than redirecting it. Proxying is very different from redirecting though, so make sure that's really the behavior you're after.

How can I hard code a json string into an *http.Response for testing purposes in Go

I have some code that makes get requests to a dynamic website, which I want to test. Obviously the tests need to be run by anyone at any time, so rather than actually use the REST API, is it possible to put a json string into an *http.Response for testing purposes.
example code:
func get (c *http.Response, err error) (string, error) {
//code
}
test file:
func TestGet(t *testing.T) {
//code to have put json string for test *http.Response
get(???, nil)
}
You want to use a bytes.Buffer to turn the data you have into an io.Reader, and NopCloser (from io/ioutil) to make it an io.ReadCloser
r := &http.Response{
Status: "200 OK",
StatusCode: 200,
// etc., lots more fields needed here.
Body: ioutil.NopCloser(bytes.NewBufferString(json))
}
If you have your json in a []byte, then use NewBuffer instead of NewBufferString.

How to send post data value in go-wrk command?

In curl, I can send post data by -d flag like below example
curl -X POST -d'{"accountID":"1"}' localhost:1234/geInfo
How am I supposed to send accountID value in go-wrk command for a post request?
Unless I am mistaken, it is (currently) not supported to pass post parameters.
I figured that from the code of go-wrk by following the -m="POST" parameter which suggests otherwise. (Offering the method "POST" of course does not imply you can also pass parameters)
The parameter is parsed in main.go:19:
method = flag.String("m", "GET", "the http request method")
then passed on to client in single_node.go:16:
go StartClient(
toCall,
*headers,
*method,
*disableKeepAlives,
responseChannel,
wg,
*totalCalls,
)
where it is received in third place in "meth" variable (client.go:14):
func StartClient(url_, heads, meth string, dka bool, responseChan chan *Response, waitGroup *sync.WaitGroup, tc int) {
and then used here (client.go:55):
req, _ := http.NewRequest(meth, url_, nil)
sets := strings.Split(heads, "\n")
//Split incoming header string by \n and build header pairs
for i := range sets {
split := strings.SplitN(sets[i], ":", 2)
if len(split) == 2 {
req.Header.Set(split[0], split[1])
}
}
timer := NewTimer()
for {
timer.Reset()
resp, err := tr.RoundTrip(req)
respObj := &Response{}
(...)
responseChan <- respObj
}
If post parameters would be passable, they would have to be put somewhere into the Request as you can lookup on the golang http package website:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest returns a new Request given a method, URL, and optional body.

How to log response body in gin

I need to log the response body in a middleware of gin, but I don't find how to get the response body. Can anyone help?
I am using a middleware like this:
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
statusCode := c.Writer.Status()
if statusCode >= 400 {
//ok this is an request with error, let's make a record for it
//log body here
}
}
}
My question is, how to get response body from Context in middleware?
You need to intercept writing of response and store it somewhere first. Then you can log it. And to do that you need to implement your own Writer intercepting Write() calls.
For example, as follows:
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func ginBodyLogMiddleware(c *gin.Context) {
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
statusCode := c.Writer.Status()
if statusCode >= 400 {
//ok this is an request with error, let's make a record for it
// now print body (or log in your preferred way)
fmt.Println("Response body: " + blw.body.String())
}
}
Then use this middleware like this:
router.Use(ginBodyLogMiddleware)
Note that this sill won't work for static files as gin does not seem to use c.Writer for them. But in most cases, that's what you want anyway.
If you want to intercept all files, you need to use a slightly more complicated approach. Instead of Middleware, you'll need to implement a wrapper http.Handler that will wrap gin.Engine and will use same approach as shown above to intercept and log whatever is written to http.ResponseWriter. Then run gin server like this:
ginRouter := gin.New()
// configure your middleware and routes as required
// Run http server as follows, where bodyLogHandler is your wrapper handler
http.ListenAndServe(bindAddress, &bodyLogHandler{wrappedHandler: ginRouter}
FYI
Note: implement WriteString() if using c.String() for writing response body
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w bodyLogWriter) WriteString(s string) (int, error) {
w.body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}
func ginBodyLogMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
fmt.Println("Response body: " + blw.body.String())
}
}
...
// register
router := r.Group("/", ginBodyLogMiddleware())

gin/golang - Empty Req Body

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)
}
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.

Resources