How do I send a JSON string in a POST request in Go

JsonRestGoApiary

Json Problem Overview


I tried working with Apiary and made a universal template to send JSON to mock server and have this code:

package main

import (
	"encoding/json"
	"fmt"
	"github.com/jmcvetta/napping"
	"log"
	"net/http"
)

func main() {
	url := "http://restapi3.apiary.io/notes"
	fmt.Println("URL:>", url)

	s := napping.Session{}
	h := &http.Header{}
	h.Set("X-Custom-Header", "myvalue")
	s.Header = h

	var jsonStr = []byte(`
{
    "title": "Buy cheese and bread for breakfast."
}`)

	var data map[string]json.RawMessage
	err := json.Unmarshal(jsonStr, &data)
	if err != nil {
		fmt.Println(err)
	}

	resp, err := s.Post(url, &data, nil, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("response Status:", resp.Status())
	fmt.Println("response Headers:", resp.HttpResponse().Header)
	fmt.Println("response Body:", resp.RawText())

}

This code doesn't send JSON properly, but I don't know why. The JSON string can be different in every call. I can't use Struct for this.

Json Solutions


Solution 1 - Json

I'm not familiar with napping, but using Golang's net/http package works fine (playground):

func main() {
	url := "http://restapi3.apiary.io/notes"
	fmt.Println("URL:>", url)

	var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("X-Custom-Header", "myvalue")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	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))
}

Solution 2 - Json

you can just use post to post your json.

values := map[string]string{"username": username, "password": password}

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))

Solution 3 - Json

If you already have a struct.

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
)

// .....

type Student struct {
	Name    string `json:"name"`
	Address string `json:"address"`
}

// .....

body := &Student{
	Name:    "abc",
	Address: "xyz",
}

payloadBuf := new(bytes.Buffer)
json.NewEncoder(payloadBuf).Encode(body)
req, _ := http.NewRequest("POST", url, payloadBuf)

client := &http.Client{}
res, e := client.Do(req)
if e != nil {
	return e
}

defer res.Body.Close()

fmt.Println("response Status:", res.Status)
// Print the body to the stdout
io.Copy(os.Stdout, res.Body)

Full gist.

Solution 4 - Json

In addition to standard net/http package, you can consider using my GoRequest which wraps around net/http and make your life easier without thinking too much about json or struct. But you can also mix and match both of them in one request! (you can see more details about it in gorequest github page)

So, in the end your code will become like follow:

func main() {
	url := "http://restapi3.apiary.io/notes"
	fmt.Println("URL:>", url)
	request := gorequest.New()
	titleList := []string{"title1", "title2", "title3"}
	for _, title := range titleList {
		resp, body, errs := request.Post(url).
			Set("X-Custom-Header", "myvalue").
			Send(`{"title":"` + title + `"}`).
			End()
		if errs != nil {
			fmt.Println(errs)
			os.Exit(1)
		}
		fmt.Println("response Status:", resp.Status)
		fmt.Println("response Headers:", resp.Header)
		fmt.Println("response Body:", body)
	}
}

This depends on how you want to achieve. I made this library because I have the same problem with you and I want code that is shorter, easy to use with json, and more maintainable in my codebase and production system.

Solution 5 - Json

Use io.Pipe for large request bodies as mentioned in another answer. This approach avoids building the entire request body in memory by streaming the data from the JSON encoder to the network.

This answer builds on the other answer by showing how to handle errors. Always handle errors!

  • Use the pipe's CloseWithError function to propagate encoding errors back to error returned from http.Post.
  • Handle the error returned from http.Post
  • Close the response body.

Here's the code:

r, w := io.Pipe()

go func() {
	w.CloseWithError(json.NewEncoder(w).Encode(data))
}()

// Ensure that read side of pipe is closed. This
// unblocks goroutine in scenario where http.Post
// errors out before reading the entire request body.
defer r.Close()

resp, err := http.Post(url, r)
if err != nil {
	// Adjust error handling here to meet application requrirements.
	log.Fatal(err)
}
defer resp.Body.Close()
// Use the response here.

Solution 6 - Json

Example post request for http or https

    //Encode the data
       postBody, _ := json.Marshal(map[string]string{
          "name":  "Test",
          "email": "[email protected]",
       })
       responseBody := bytes.NewBuffer(postBody)
    //Leverage Go's HTTP Post function to make request
       resp, err := http.Post("https://postman-echo.com/post", "application/json", responseBody)
    //Handle Error
       if err != nil {
          log.Fatalf("An Error Occured %v", err)
       }
       defer resp.Body.Close()
    //Read the response body
       body, err := ioutil.ReadAll(resp.Body)
       if err != nil {
          log.Fatalln(err)
       }
       sb := string(body)
       log.Printf(sb)

Solution 7 - Json

If you have a lot of data to send, you can use a pipe:

package main

import (
   "encoding/json"
   "io"
   "net/http"
)

func main() {
   m := map[string]int{"SNG_ID": 75498415}
   r, w := io.Pipe()
   go func() {
      json.NewEncoder(w).Encode(m)
      w.Close()
   }()
   http.Post("https://stackoverflow.com", "application/json", r)
}

https://golang.org/pkg/io#Pipe

Solution 8 - Json

if you want to do it like that, you need to use this map for unmarshalling json string.

var data map[string]interface{}

but if you need to change the json each time and to make initialization of your requst body more convenient, you can use this map for creating json body.

var bodyJsonMap map[string]interface{}{
    "key1": val1,
    "key2": val2,
    ...
}

Then marshal it to a json-string.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLadislav PrskavecView Question on Stackoverflow
Solution 1 - JsonOneOfOneView Answer on Stackoverflow
Solution 2 - JsongaozhidfView Answer on Stackoverflow
Solution 3 - JsonNinh PhamView Answer on Stackoverflow
Solution 4 - JsonA-letubbyView Answer on Stackoverflow
Solution 5 - Jsonuser13631587View Answer on Stackoverflow
Solution 6 - JsonmesutpiskinView Answer on Stackoverflow
Solution 7 - JsonZomboView Answer on Stackoverflow
Solution 8 - JsonHighdegerView Answer on Stackoverflow