How to read from array json response in Go - go

I have an API request that returns a refresh_token inside array, which looks something like this:
[
{
"refresh_token" : "C61551CEA183EDB767AA506926F423B339D78E2E2537B4AC7F8FEC0C29988819"
}
]
I need to access this refresh_token's value, and use it to query another API.
To do this, I'm attempting to first 'ReadAll' the response body, and then access the key inside of it by calling 'refreshToken'.
However, it's not working. Does anyone know how to resolve this as I can't figure it out?
Here's my code:
func Refresh(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
// q := url.Values{}
fetchUrl := "https://greatapiurl.com"
req, err := http.NewRequest("GET", fetchUrl, nil)
if err != nil {
fmt.Println("Errorrrrrrrrr")
os.Exit(1)
}
req.Header.Add("apikey", os.Getenv("ENV"))
req.Header.Add("Authorization", "Bearer "+os.Getenv("ENV"))
resp, err := client.Do(req)
if err != nil {
fmt.Println("Ahhhhhhhhhhhhh")
os.Exit(1)
}
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println(respBody["refresh_token"])
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}

If you do not need it as custom type you can cast it as []map[string]string
respBody, _ := ioutil.ReadAll(resp.Body)
var body []map[string]string
json.Unmarshal(respBody, &body)
fmt.Println(body[0]["refresh_token"])

Related

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 GET Request with Cookie After login

I tried to get some resp.Body of jadwalURL. jadwalURL can be access after login, so I add the Cookie header to the request. But Sadly the response is not quite that I want (response is home page). I tried this similiar flow with Postman. and I got the jadwalURL body as i wanted. Is there anything wrong with my code? I still dont get the solution after 3 hours searching.
func main() {
data := url.Values{}
data.Set("username", username)
data.Set("password", password)
client := &http.Client{}
r, _ := http.NewRequest(http.MethodPost, loginURL, strings.NewReader(data.Encode()))
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, _ := client.Do(r)
cookie := resp.Cookies()
fmt.Println(cookie)
fmt.Println(resp.Status)
req, err := http.NewRequest(http.MethodGet, jadwalURL, nil)
if err != nil {
panic(err)
}
req.AddCookie(&http.Cookie{
Name: cookie[0].Name,
Value: cookie[0].Value,
Domain: domainURL,
Path: "/",
})
jadwalResp, err := http.DefaultClient.Do(req)
if err != nil {
panic(nil)
}
body, _ := ioutil.ReadAll(jadwalReq.Body)
jadwalResp.Body.Close()
fmt.Println(string(body))
}
Hi If you have this problem I just change create a newClient the problem is i tried to make request with client.
newClient := &http.Client{}
// then
jadwalResp, err := newClient.Do(req)

How can I resolve 400 status code by sending POST in Golang?

I have Python script, that works fine
def register():
url = prime_url + '/v2/mobile/user/register?lang=ru'
payload = {
'email' : 'test#test.test.mail.com',
'deviceId' : 'testId',
'password' : 'Test1234'
}
response = requests.post(url, data = payload)
print(response.text)
Response:
{"success":true,"data":"SUCCESS_FIRST_STAGE_REGISTER","params":"Two-factor authentication code sent to test#testtest.test.mail.com","code":200,"runTime":2.391624927520752}
I wrote code on Golang:
func postRequest(target string, params string) {
var jsonStr = []byte(`{"email":"testtestetetsees#mail.ru", "deviceId":"ftefst891", "password":"qwertyQwerty132"}`)
req, err := http.NewRequest("POST", target, bytes.NewBuffer(jsonStr))
if err != nil {
log.Fatalln(err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
Response:
map[code:400 data:ERROR_VALIDATE params:map[deviceId:[Device Id cannot be blank.] email:[Email cannot be blank.] password:[Password cannot be blank.]] runTime:0.023465871810913086 success:false]
I see that problem is by sending JSON string. What should I do?
As mentioned by Peter in the comments you can use PostForm once you convert the data into url.Values.
If the server expects content in urlencoded form but the input you have is json you'll have to convert it first.
var data = []byte(`{"email":"testtestetetsees#mail.ru", "deviceId":"ftefst891", "password":"qwertyQwerty132"}`)
m := map[string]string{}
if err := json.Unmarshal(data, &m); err != nil {
panic(err)
}
v := url.Values{}
for key, val := range m {
v.Add(key, val)
}
resp, err := http.PostForm("https://example.com", v)
if err != nil {
panic(err)
}
// ...
If the input you have is already in urlencoded form but it needs escaping you can parse it using url.ParseQuery and let the result of that do the escaping.
var data = "email=testtestetetsees#mail.ru&deviceId=ftefst891&password=qwertyQwerty132"
v, err := url.ParseQuery(data)
if err != nil {
panic(err)
}
resp, err := http.PostForm("https://example.com", v)
if err != nil {
panic(err)
}
// ...
Mention content type as "application/json"
For simplicity you can use http.Post, use the below code as a example:
resp, err := http.Post(target, "application/json", bytes.NewBuffer([]byte("{\"email\":\"testtestetetsees#mail.ru\", \"deviceId\":\"ftefst891\", \"password\":\"qwertyQwerty132\"}")))

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

How to send new request with object instead of bytes?

I need to send an object of data e.g. {hello: "world", goodbye: "world"} to an API. I'm doing it like this right now:
inputs := form.GetElementsByTagName("input")
var data = make(map[string]interface{}) // after adding values this looks like this: {hello: "world", goodbye: "world"}
for value := range inputs {
// Append all values from the inputs to a new array, with the key named by the input name attribute
if inputs[value] != nil && inputs[value].(*dom.HTMLInputElement).Value != "" {
data[inputs[value].(*dom.HTMLInputElement).Name] = inputs[value].(*dom.HTMLInputElement).Value
}
}
parsedData, _ := json.Marshal(data)
req, _ := http.NewRequest(method, url, bytes.NewBuffer(parsedData))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
go func() { // Must be a goroutine
response, _ := client.Do(req)
defer response.Body.Close()
}()
The problem I'm having is since we're sending it as a byte, the server always returns error responses as it's expecting to deal with an object.
How can I make sure it's sending an object instead of bytes?
You are setting the content type to application/x-www-form-urlencoded while you are sending the data in json format, so change your content-type when setting the request headers, along with that do not skip the error to check what is the error returned:
parsedData, err := json.Marshal(data)
if err != nil{
fmt.Println(err)
}
req, err := http.NewRequest(method, url, parsedData) // send the parseData which are bytes returned from the marshal.
if err != nil{
fmt.Println(err)
}
req.Header.Set("Content-Type", "application/json") // set the content type to json
go func() { // Must be a goroutine
response, err := client.Do(req)
if err != nil{
fmt.Println(err)
}
defer response.Body.Close()
}()
// you should check for response status to verify the details as
fmt.Println("response Status:", response.Status)
fmt.Println("response Headers:", response.Header)
body, _ := ioutil.ReadAll(response.Body)
fmt.Println("response Body:", string(body))
One thing that should be taken into consideration is that you have not exported your struct fields. That can be the reason your json string becomes empty. Make your struct fields exportable by making their first letter of each field in caps.
I solved this using the "net/url" package.
data := url.Values{}
for value := range inputs {
// Append all values from the inputs to a new array, with the key named by the input name attribute
if inputs[value] != nil && inputs[value].(*dom.HTMLInputElement).Value != "" {
data.Add(inputs[value].(*dom.HTMLInputElement).Name, inputs[value].(*dom.HTMLInputElement).Value)
}
}
req, _ := http.NewRequest(method, actionUrl, strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

Resources