How to call Body.Close() when using require()? - go

API test has below boiler plate code.
I want to call resp.Body.Close() even when require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode) fails.
Currently i have script like below. Are there better ways to write this?
I want to avoid condition if tp.expectedStatus != resp.StatusCode and call resp.Body.Close() when require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode) fails.
func Invoke(ip RestParams, es int) *http.Response {
client := &http.Client{}
// Initialize req using ip
resp, err := client.Do(req)
...
if tp.expectedStatus != resp.StatusCode {
resp.Body.Close()
require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode)
return nil
}
return resp
}

You should almost always do
resp, err := client.Do(req)
if err != nil {...} // Or require.NoError(err)
defer resp.Body.Close()
http package garanties a non nil body that should be closed as soon as the error is nil.
As a side note, I think you should avoid returning the http.Response. Unmarshal it here and return a struct model so you can handle all your technical http layer in this functio.

Related

golang hangs when using multipart/form-data

I want to make an empty post request to telegram.
The problem is if i close multipart once, it hangs forever:
func main() {
var requestBody bytes.Buffer
multiPartWriter := multipart.NewWriter(&requestBody)
multiPartWriter.Close() // closing once
req, _ := http.NewRequest("POST", "https://api.telegram.org/bot<telegram token>/getme", &requestBody)
req.Header.Set("Content-Type", multiPartWriter.FormDataContentType())
client := &http.Client{}
client.Do(req)
}
But if i close the multipart twice it works.
Can anyone explain why this happens?
I just checked the Telegram API.
I guess the general problem is, that you use a buffer that is not initialized.
You don't need the buffer, you don't need any payload in the request. You can just pass nil as request data. Like this:
func main() {
req, err := http.NewRequest("POST", "https://api.telegram.org/bot<token>/getme", nil)
if err != nil {
panic(err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
println(string(result))
}
I also recommend, that you check out the docs here this documentation lets you interactively try out the API, it can also generate the code for each request.
In order to generate Go code examples, you can click on the button at the upper right corner and chose your Go.

Converting string from HTTP request to data struct in Go

I've got an HTTP Post method, which successfully posts data to an external third party API and returns a response.
I then need data returned from this response to post to my database.
The response contains a few piece of data, but I only need the 'access_token' and 'refresh_token' from it.
As a result, what I'm attempting to do is convert the response from a string into individual components in a new data struct I've created - to then pass to my database.
However, the data is showing as blank, despite it successfully being written to my browser. I'm obviously doing something fundamentally wrong, but not sure what..
Here's my code:
type data struct {
Access_token string `json:"access_token"`
Refresh_token string `json:"refresh_token"`
}
func Fetch(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
q := url.Values{}
q.Add("grant_type", "authorization_code")
q.Add("client_id", os.Getenv("ID"))
q.Add("client_secret", os.Getenv("SECRET"))
q.Add("redirect_uri", "https://callback-url.com")
q.Add("query", r.URL.Query().Get("query"))
req, err := http.NewRequest("POST", "https://auth.truelayer-sandbox.com/connect/token", strings.NewReader(q.Encode()))
if err != nil {
log.Print(err)
fmt.Println("Error was not equal to nil at first stage.")
os.Exit(1)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request to server")
os.Exit(1)
}
respBody, _ := ioutil.ReadAll(resp.Body)
d := data{}
err = json.NewDecoder(resp.Body).Decode(&d)
if err != nil {
fmt.Println(err)
}
fmt.Println(d.Access_token)
fmt.Println(d.Refresh_token)
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}
With ioutil.ReadAll you read the body, already. The second time you pass to NewDecoder(resp.Body) the stream was consumed.
You can use instead json.Unmarshal(respBody, &d).
One more advice, don't ignore the error on ioutil.ReadAll

Returning data back to client from Go HTTP request

I've written a simple Fetch Go function which calls an API, and generates a response.
When called, it successfully logs the data to the console which is pulled from the API.
What I want to do though is take the final 'respBody' variable generated from reading the response body, and then return it back to my frontend client - but I can't figure out how.
All the examples just use Println, and I've searched the docs but can't find anything.
Can anyone tell me how to change my code so I can return the respBody back to the client?
Here's my function:
func Fetch(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest", nil)
if err != nil {
log.Print(err)
os.Exit(1)
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request to server")
os.Exit(1)
}
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(respBody)) // This is the final bit where I want to send this back to the client.
}
Your function is a HandlerFunc, which contains the ResponseWriter interface, in your case it's w.
So, you can write data using http.ResponseWriter:
func Fetch(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest", nil)
if err != nil {
log.Print(err)
os.Exit(1)
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request to server")
os.Exit(1)
}
respBody, _ := ioutil.ReadAll(resp.Body)
// Here:
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}
You can use use io.Copy(w, resp.Body) instead, remember to close body using defer resp.Body.Close().
You can simply copy the contents of the response body to the response writer:
io.Copy(w,resp.Body)
Since you can only read the body once, the solution above will not allow you to get the body. If you also want to log it, or process it somehow, you can read it and then write it to the response writer.
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(respBody))
w.Write(respBody)

How to return a body from another request

req, err := http.NewRequest("GET", "https://api.github.com/repos/octocat/Hello-World/pulls/1347", nil)
req.Header.Set("Accept", "application/vnd.github.v3.patch")
if err != nil {
check(err)
}
body, err := ioutil.ReadAll(req.Body)
ctxt.JSON(http.StatusOK, body)
Here I need to send api response from body of github api. But here I'm getting the following error:
"runtime error: invalid memory address or nil pointer dereference"
You're creating a new GET request with a nil body. See the function signature for http.NewRequest
func NewRequest(method, url string, body io.Reader) (*Request, error)
so when you access resp.Body, of course it's going to be nil.
Also, http.NewRequest just returns a request, it doesn't actually perform it.
To actually make the GET request with your request, you need to pass it to a http client's Do method. Like so:
response, err := http.DefaultClient.Do(resp)
EDIT: I would also add that naming your request as resp is confusing. I would recommend renaming the variable to req or request
this code will solve your issues.
client := &http.Client{}
apiURL := "https://api.github.com/repos/octocat/Hello-World/pulls/1347"
req, err := http.NewRequest("GET", apiURL, nil)
if err != nil {
check(err)
}
req.Header.Add("Accept", "application/vnd.github.v3.patch")
response, err := client.Do(req)
if err != nil {
check(err)
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
if err != nil {
check(err)
}
ctxt.JSON(http.StatusOK, string(contents))

Should I error-check Close() on a response body?

The docs for net/http have the following example:
resp, err := http.Get("http://example.com/")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("%s", body)
Close returns an error, but it is not checked. Is there something I'm missing here? The importance of checking every error is frequently emphasized in go, but I see this defer resp.Body.Close() pattern a lot with no error checks.
There are two things to consider: What would you do with it if you checked it and there was an error? And, what would the side-effects be if there was an error?
In most cases, for closing a response body, the answer to both questions is... absolutely nothing. If there's nothing you'd do if there was an error and the error has no appreciable impact, there's no reason to check it.
Also note that Close() returns an error in order to fulfill the io.Closer interface; the function doesn't necessarily return an error. You'd need to check the source to know for sure if it has an error case.
This is a downside of using defer
It would be advised, as a responsible developer, that you check for all possible error prone points, and handle them as gracefully as you can.
Here are some of the options you can choose in handling this situation:
Option #1
Do not use defer, instead manually call close once you're done with the response's body and simply check for errors then.
Option #2
Create an anonymous function that wraps the closing and error checking code. Something like this:
defer func() {
err := resp.Body.Close()
if err != nil {
log.Fatal(err)
}
}()
Avoid using panics in your programs. Try to handle the errors gracefully by doing something or at least logging the error.
Additional information can be found here.
To add to #Mihailo option #2, call it option #2.1
Define function dclose() like so:
func dclose(c io.Closer) {
if err := c.Close(); err != nil {
log.Fatal(err)
}
}
use like so:
defer dclose(resp.Body)
Also in your code the check for err!=nil can declare:
func errcheck(err error) {
if err != nil {
log.Fatal(err)
}
}
then use:
errcheck(err)
then your code becomes:
resp, err := http.Get("http://example.com/")
errcheck(err)
defer dclose(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
errcheck(err)
fmt.Printf("%s", string(body))
IMHO a little cleaner perhaps? But I'll wait for the go aficionado's to correct me and highlight drawbacks.
EDIT
Thanks! #RayfenWindspear
Do replace log.Fatal(err) with log.Println(err) to avoid unnecessary panic.
EDIT2
dclose() to avoid confusion with go close()
Have fun!
From what I could gather, net/http can't err. However, I would rather assume that .Close() can err for any implementation of io.Closer than to study the internals.
Below is an example using named returns where defer only sets the returned error if the error would otherwise be nil:
func printResponse(url string) (retErr error) {
resp, err := http.Get(url)
if err != nil {
return err
}
defer func() {
err := resp.Body.Close()
if err != nil && retErr == nil {
retErr = err
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Printf("%s", body)
return nil
}

Resources