I am getting the below response in a URL and I want to unmarshal it, but I am unable to do so.
This is the kind of response that I'd like to unmarshal.
[
{"title": "Angels And Demons", "author":"Dan Brown", "tags":[{"tagtitle":"Demigod", "tagURl": "/angelDemon}] }
{"title": "The Kite Runner", "author":"Khalid Hosseinei", "tags":[{"tagtitle":"Kite", "tagURl": "/kiteRunner"}] }
{"title": "Dance of the dragons", "author":"RR Martin", "tags":[{"tagtitle":"IronThrone", "tagURl": "/got"}] }
]
I am trying to unmarshal this sort of response but not being able to do so. This is the code that I am trying to write.
res, err := http.Get(url)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Fatal("Couldn't get the html response")
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Fatal("Couldn't read the response")
}
s := string(b)
var data struct {
Content []struct {
Title string `json:"title"`
Author string `json:"author"`
Tags map[string]string `json:"tags"`
}
}
if err := json.Unmarshal([]byte(s), &data); err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Un-marshalling could not be done.")
}
fmt.Println(data.Content)
Can anyone please help me in this regard?
Thanks in advance.
Change it
var data struct {
Content []struct {
Title string `json:"title"`
Author string `json:"author"`
Tags map[string]string `json:"tags"`
} }
to this
type Content struct {
Title string `json:"title"`
Author string `json:"author"`
Tags map[string]string `json:"tags"`
}
var data []Content
Consider unmarshalling it into a slice of Content:
type Content struct {
Title string `json:"title"`
Author string `json:"author"`
Tags map[string]string `json:"tags"`
}
// Send in your json
func convertToContent(msg string) ([]Content, error) {
content := make([]Content, 0, 10)
buf := bytes.NewBufferString(msg)
decoder := json.NewDecoder(buf)
err := decoder.Decode(&content)
return content, err
}
Check out an example with your use case here: http://play.golang.org/p/TNjb85XjpP
I was able to sort out this issue by doing a simple amendment in the above code.
var Content []struct {
Title string `json:"title"`
Author string `json:"author"`
Tags map[string]string `json:"tags"`
}
Thanks for all your responses.
Related
I have a Gin program. When a request comes, I want all of the fields of the variable data (type ProductCreate) to have values: UserId (from headers) and Name, Price (from JSON body). I used the below code and it works:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
UserId int `header:"user-id"`
Name string `json:"name"`
Price int `json:"price"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
var data ProductCreate
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(&data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}
After that, I wanted to make sure that fields must be provided so I edited the ProductCreate struct like this:
type ProductCreate struct {
UserId int `header:"user-id" binding:"required"`
Name string `json:"name" binding:"required"`
Price int `json:"price" binding:"required"`
}
Then it raised an unexpected error when I tested it again:
Key: 'ProductCreate.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'ProductCreate.Price' Error:Field validation for 'Price' failed on the 'required' tag
I realized the error happened at this:
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
Is there any solution to resolve my problem?
Can you try this?
curl --location --request POST 'http://localhost:8080/product' \
--header 'user-id: 20' \
--data-raw '{
"name": "sr"
}'
I tried your code and it works perfectly.
{
"UserId": 20,
"name": "sr",
"price": 0
}
Gin version: github.com/gin-gonic/gin v1.8.1 // indirect
Soln:
package main
import (
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
Name *string `json:"name" binding:"required"`
Price *int `json:"price" binding:"required"`
}
type Header struct {
UserId *int `header:"user-id" binding:"required"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
data := &ProductCreate{}
header := &Header{}
// bind the headers to data
if err := c.ShouldBindHeader(header); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}
I am trying to get the parameters made in a POST request, but I am not able to make it, my code is:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", hello)
fmt.Printf("Starting server for testing HTTP POST...\n")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "404 not found.", http.StatusNotFound)
return
}
switch r.Method {
case "POST":
// Call ParseForm() to parse the raw query and update r.PostForm and r.Form.
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
name := r.Form.Get("name")
age := r.Form.Get("age")
fmt.Print("This have been received:")
fmt.Print("name: ", name)
fmt.Print("age: ", age)
default:
fmt.Fprintf(w, "Sorry, only POST methods are supported.")
}
}
I am making the POST request in the terminal as follows:
curl -X POST -d '{"name":"Alex","age":"50"}' localhost:8080
And then the output is:
This have been received:name: age:
Why it is not taking the parameters? What I am doing wrong?
As you pass your body as a json object, you better define a Go struct matching that object and decode the request body to the object.
type Info struct {
Name string
Age int
}
info := &Info{}
if err := json.NewDecoder(r.Body).Decode(info); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(info)
You can find the whole working code here.
$ curl -X POST -d '{"name":"Alex","age":50}' localhost:8080
This POST request is working fine now.
You could modify the Go struct and also the response object as you like .
I am trying to send the following data via a POST HTTP request to an API:
{
"client_interface":{
"source_address":source,
"destination_address":destn,
"message":encrypted_msg,
"business_event_url":settings.Message_CallbackURL
},
"server_interface":{
"message_id":msg_id
}
}
The API is responding with the following error:
{
"Meta":{
"Requestid":12301343169471000
},
"Error":{
"Message":"Request body contains badly-formed JSON (at position 51)",
"Param":""
}
}
CODE:
apiUrl := "http://example.com"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
}
jsonStr := []byte(`{
"client_interface": {
"source_address": source,
"destination_address": destn,
"message": encrypted_msg,
"business_event_url": settings.Message_CallbackURL
},
"server_interface": {
"message_id": msg_id
}
}`)
req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonStr))
fmt.Println("req..........",req)
if err!=nil{
log.Println("err in http req..............",err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", msg_id)
req.Header.Set("Authorization", "Bearer "+conn_token)
client := &http.Client{Transport: tr}
resp, err := client.Do(req)
if resp!=nil{
body, _ := ioutil.ReadAll(resp.Body)
}
Using struct :
package main
import (
"fmt"
"net/http"
"io/ioutil"
"bytes"
//"crypto/tls"
"encoding/json"
)
type client_interface struct {
source_address string `json:"string"`
destination_address uint64 `json:"uint64"`
message string `json:"string"`
business_event_url string `json:"string"`
}
type server_interface struct {
message_id uint64 `json:"uint64"`
}
type data struct {
client_interface client_interface `json:"client_interface"`
server_interface server_interface `json:"server_interface"`
}
func main() {
url := "https://example.com"
fmt.Println("URL:>", url)
client_interface := client_interface{}
server_interface := server_interface{}
client_interface.source_address="1"
client_interface.destination_address=1111111111
client_interface.message="khsjhdjks"
client_interface.business_event_url="http://callbackurl-hdfc"
server_interface.message_id=8210993557215399651
fmt.Println("server_interface..........",server_interface)
fmt.Println("client_interface..........",client_interface)
body1 := &data{
client_interface: client_interface,
server_interface: server_interface,
}
fmt.Println("body1..........",body1)
t,e:=json.Marshal(body1)
fmt.Println("t..........",t)
fmt.Println("e..........",e)
req, err := http.NewRequest("POST", url, bytes.NewReader(t))
fmt.Println("req......",req)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", "8210993557215399651")
req.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA")
client := &http.Client{}
resp, err := client.Do(req)
fmt.Println("err.............",err)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response Body:", string(body))
}
First of all: please use gofmt
Your first code can't work because golang doesn't substitute variables inside string. It's better to use structs.
With structs it's not working because you named struct fields from lower case latter, it means "private" fields in go. So encoding/json package can't access them and just skip them. Use Capital first letters.
Another fix is about 'json:"tag"' - here tag means encoded field name, not type. So instead of 'json:"string"' you should use 'json:"message_id"' or so. You can specify type like this 'json:"field_name,type"' or like this 'json:",type"' but encoding/json guess type on his own.
(I used wrong quotes in tags because of markdown)
I used netcat -l 5000 to listen on 5000 port on localhost and print everything to the terminal. Then I changed url to http://localhost:5000 (not https) to send request to myself.
You need to restart netcat each time to work.
And I made logging a bit more readable.
Also it's CamelCase naming convention in go.
Changed your code a little bit
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"net/http"
//"crypto/tls"
"encoding/json"
"github.com/nikandfor/tlog"
)
type ClientInterface struct {
SourceAddress string `json:"source_address"`
DestinationAddress uint64 `json:"destination_address"`
Message string `json:"message"`
BusinessEventURL string `json:"business_event_url"`
}
type ServerInterface struct {
MessageID uint64 `json:"message_id"`
}
type Data struct {
ClientInterface ClientInterface `json:"client_interface"`
ServerInterface ServerInterface `json:"server_interface"`
}
var (
// use command line flag. so run like so:
// go run ./file.go -addr https://example.com
addr = flag.String("addr", "http://localhost:5000", "address to send data to")
)
func main() {
flag.Parse() // DO NOT FORGET TO PARSE FLAGS
fmt.Println("URL:>", *addr)
clientInterface := ClientInterface{
SourceAddress: "1",
DestinationAddress: 8886121111,
Message: "khsjhdjks",
BusinessEventURL: "http://callbackurl-hdfc",
}
serverInterface := ServerInterface{
MessageID: 8210993557215399651,
}
tlog.Printf("server_interface %+v", serverInterface)
tlog.Printf("client_interface %+v", clientInterface)
body1 := &Data{
ClientInterface: clientInterface,
ServerInterface: serverInterface,
}
tlog.Printf("body %+v", body1)
t, err := json.Marshal(body1)
if err != nil {
panic(err)
}
tlog.Printf("marshalled: %s", t)
req, err := http.NewRequest("POST", *addr, bytes.NewReader(t))
tlog.Printf("req %v", req)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", "8210993557215399651")
req.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
tlog.Printf("response Status: %v", resp.Status)
tlog.Printf("response Headers: %v", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
tlog.Printf("response Body: %s", string(body))
}
$ nc -l 5000
POST / HTTP/1.1
Host: localhost:5000
User-Agent: Go-http-client/1.1
Content-Length: 92
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA
Content-Type: application/json
Requestid: 8210993557215399651
Accept-Encoding: gzip
{"client_interface":{"source_address":"1","destination_address":8886121111,"message":"khsjhdjks","business_event_url":"http://callbackurl-hdfc"},"server_interface":{"message_id":8210993557215399651}}
Is it what you've expected?
And the last. I strongly suggest you to read https://golang.org/doc/effective_go.html
I want to construct a HTTP POST request with a body using Go net/http library.
The function I am using to construct the http request is the following: docs
http.NewRequest(method string, url string, body io.Reader)
I came up with 2 solutions, but I am trying to see which one is more idiomatic and extensible to support different body configurations.
Solution #1
bytesObj := []byte(`{"key":"value"}`)
body := bytes.NewBuffer(bytesObj)
Solution #2
bodyMap := map[string]string{"key":"value"}
bodyBytes, _ := json.Marshal(bodyMap)
body := bytes.NewBuffer(bodyBytes)
Ideally, I will move the code to a helper function that way I can customize the construction of the body. The helper function will be something like
func constructBody(someArgument) io.Reader {
return bodyHere
}
If the body is already string, options #1 is more compelling to me.
If you are only working with a key -> value with only string, option #2 is better.
But this will become cumbersome when you have nested struct
But most of the time in my experience we are dealing with struct. I like to make the struct closer to where the http call happened.
func main() {
ctx := context.Background()
body := struct {
Key string `json:"key"`
}{
Key: "value",
}
out, err := json.Marshal(body)
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("POST", "http://example.com", bytes.NewBuffer(out))
if err != nil {
log.Fatal(err)
}
req = req.WithContext(ctx)
http.DefaultClient.Do(req)
}
And if the struct is used in multiple places, you can make a package level struct.
To have it more reusable, you could create a jsonReaderFactory, which just takes a few lines of code:
func jsonReaderFactory(in interface{}) (io.Reader, error) {
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)
err := enc.Encode(in)
if err != nil {
return nil, fmt.Errorf("creating reader: error encoding data: %s", err)
}
return buf, nil
}
Example on playground
New to golang. I'm trying to make a POST request to an auth endpoint to get back a token for authing further requests. Currently the error I'm getting is missing "credentials". I've written the same logic in Python so I know what I am trying to do is what the system is expecting.
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"os"
)
type Auth struct {
Method string `json:"credentials"`
Email string `json:"email"`
Password string `json:"password"`
Mfa string `json:"mfa_token"`
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Email: ")
e, _ := reader.ReadString('\n')
fmt.Print("Enter Password: ")
p, _ := reader.ReadString('\n')
fmt.Print("Enter 2FA Token: ")
authy, _ := reader.ReadString('\n')
auth := Auth{"manual", e, p, authy}
j, _ := json.Marshal(auth)
jar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: jar,
}
req, err := http.NewRequest("POST", "https://internaltool.com/v3/sessions", bytes.NewBuffer(j))
if err != nil {
log.Fatal(err)
}
req.Header.Add("Accept-Encoding", "gzip, deflate, br")
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
s := string(body)
if res.StatusCode == 400 {
fmt.Println("Bad Credentials")
fmt.Println(s)
return
}
}
The question is - am I properly marshalling the AUTH struct into JSON and adding it appropriately to the POST request? As the API is not even seeing the credentials key in the JSON I think I must be doing something wrong. Anything helps.
Here's a minimum viable example of using json.Marshal to convert a Struct to a JSON object in the context of a POST request.
Go's standard libraries are fantastic, there is no need to pull in external dependencies to do such a mundane thing.
func TestPostRequest(t *testing.T) {
// Create a new instance of Person
person := Person{
Name: "Ryan Alex Martin",
Age: 27,
}
// Marshal it into JSON prior to requesting
personJSON, err := json.Marshal(person)
// Make request with marshalled JSON as the POST body
resp, err := http.Post("https://httpbin.org/anything", "application/json",
bytes.NewBuffer(personJSON))
if err != nil {
t.Error("Could not make POST request to httpbin")
}
// That's it!
// But for good measure, let's look at the response body.
body, err := ioutil.ReadAll(resp.Body)
var result PersonResponse
err = json.Unmarshal([]byte(body), &result)
if err != nil {
t.Error("Error unmarshaling data from request.")
}
if result.NestedPerson.Name != "Ryan Alex Martin" {
t.Error("Incorrect or nil name field returned from server: ", result.NestedPerson.Name)
}
fmt.Println("Response from server:", result.NestedPerson.Name)
fmt.Println("Response from server:", result.NestedPerson.Age)
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// NestedPerson is the 'json' field of the response, what we originally sent to httpbin
type PersonResponse struct {
NestedPerson Person `json:"json"` // Nested Person{} in 'json' field
}
As http.Client is relatively a low-level abstraction, gorequest(https://github.com/parnurzeal/gorequest) as an alternative is strongly recommended.
headers, queries, and body can be posted in any type, which is a bit more like what we often do in Python.