How to send a POST request in Go? - go

I am trying to make a POST request but I can't get it done. Nothing is received on the other side.
Is this how it is supposed to work? I'm aware of the PostForm function but I think I can't use it because it can't be tested with httputil, right?
hc := http.Client{}
req, err := http.NewRequest("POST", APIURL, nil)
form := url.Values{}
form.Add("ln", c.ln)
form.Add("ip", c.ip)
form.Add("ua", c.ua)
req.PostForm = form
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
glog.Info("form was %v", form)
resp, err := hc.Do(req)

You have mostly the right idea, it's just the sending of the form that is wrong. The form belongs in the body of the request.
req, err := http.NewRequest("POST", url, strings.NewReader(form.Encode()))

I know this is old but this answer came up in search results. For the next guy - the proposed and accepted answer works, however the code initially submitted in the question is lower-level than it needs to be. Nobody got time for that.
//one-line post request/response...
response, err := http.PostForm(APIURL, url.Values{
"ln": {c.ln},
"ip": {c.ip},
"ua": {c.ua}})
//okay, moving on...
if err != nil {
//handle postform error
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
//handle read response error
}
fmt.Printf("%s\n", string(body))
https://golang.org/pkg/net/http/#pkg-overview

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

Unexpected error response from GitHub API 422 when attempting to create issue

When posting an issue to GitHub API V3 I am getting an unexpected response. Namely 422 Unprocessable Entity. However the detail of the error is for the Search endpoint rather that the POST create endpoint.
{"message":"Validation Failed","errors":[{"resource":"Search","field":"q","code":"missing"}],"documentation_url":"https://developer.github.com/v3/search"}
My instinct is that I have messed up the json but it is pretty simple and I can't see the issue.
I have tried various solutions posted here and elsewhere but not found what I am doing wrong. This is a coding exercise rather than anything intended for production but driving me mildly insane.
Tested in Debug what the Request body is just before being posted.
{"title":"Hello World","body":"dfsdfsdf\n"}
Tried removing the body as it is optional, same issue.
Tested in Debug that request is of type POST
Tested in Debug that authorization header is correct.
Removed authorization key and received 401 as expected.
The posting function:
func CreateIssue (issue *NewIssue) (*IssueDetailsResult, error){
issueJson, err := json.Marshal(issue)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
req, err := http.NewRequest("POST", github.IssuesURL, bytes.NewBuffer(issueJson))
req.Header.Set("Authorization", "token "+os.Getenv("UPGITUSER"))
req.Header.Set( "Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
if resp.StatusCode != http.StatusCreated {
bodyBytes, _ := ioutil.ReadAll(resp.Body)
body := string(bodyBytes)
resp.Body.Close()
return nil, fmt.Errorf("create issue failed:%s", resp.Status + "\ntext: " + body)
}
var result IssueDetailsResult
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
resp.Body.Close()
return nil, err
}
resp.Body.Close()
return &result, nil
}
Would expect 201 from GitHubAPI.
The response is a strong indicator that the request is being sent to the wrong endpoint.
You can use net/http/httputil's DumpRequestOut to inspect the requests you are about to send and to ensure that they are what you expect them to be.

Resources