I’ve been doing a lot of development in Golang lately. One of the things that has always been a sore spot was interfacing with other web services, both on the sending and receiving side of things. It can be cumbersome tossing all your values into a fmt.Printf(“%+v”, req) when troubleshooting. The output is not easy to read.
To help make this simpler, I created the following function which will take a *http.Request object and print out a readable version of it.// formatRequest generates ascii representation of a request
func formatRequest(r *http.Request) string {
// Create return string
var request []string// Add the request string
url := fmt.Sprintf(“%v %v %v”, r.Method, r.URL, r.Proto)
request = append(request, url)// Add the host
request = append(request, fmt.Sprintf(“Host: %v”, r.Host))// Loop through headers
for name, headers := range r.Header {
name = strings.ToLower(name)
for _, h := range headers {
request = append(request, fmt.Sprintf(“%v: %v”, name, h))
}
}
// If this is a POST, add post data
if r.Method == “POST” {
r.ParseForm()
request = append(request, “\n”)
request = append(request, r.Form.Encode())
}// Return the request as a string
return strings.Join(request, “\n”)
}
The output would appear as the following:POST https://accounts.google.com/o/oauth2/token HTTP/1.1
Host: accounts.google.com
content-type: application/x-www-form-urlencodedclient_id=ssssss&client_secret=sssss&grant_type=authorization_code&redirect_uri=ssssss&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile
One little catch is, if you read the r.Body of an outgoing request, you will have read the body from the buffer. This means, that your request will most likely fail, as the Content-Length header that is sent will have the original length of the request. The actual length of the request body will be 0.
To get around this, you can read the body into a buffer, and then write it back after you print out the request:// Buffer the body
bodyBuffer, _ := ioutil.ReadAll(r.Body)// Put the body back for FormatRequest to read it
req.Body = myReader{bytes.NewBuffer(buf)}
fmt.Printf("--> %s\n\n", formatRequest(req))// Put it back before you call client.Do()
req.Body = myReader{bytes.NewBuffer(buf)}
Hopefully this makes your life a lot easier when troubleshooting web services in Golang.
Update: 2016/03/21
Thanks to Lars Grote for pointing out that the httputil package has a DumpRequest method pre-baked. The output is nearly exactly the same which is great since it removes the dependency on 3rd party code.
To use it correctly, you would:// Save a copy of this request for debugging.
requestDump, err := httputil.DumpRequest(req, true)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(requestDump))
The req argument is the http.Request, and the true lets the function know if there is a request body as well.

Member discussion