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

Go To StackoverFlow.com

166

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.

2014-06-27 15:09
by Ladislav Prskavec
I'm not familiar with some of the libraries you use, but as I understand it, you are trying to send a map of Jsons. Why don't you just send the string with the json - Topo 2014-06-27 15:28
why are you unmarshaling the json if you want to send json - JimB 2014-06-27 15:30
A little tip, you can create your message as a struct or map[string]interface{} to add all the values you want and then use json.Marshall to convert the map or struct to a json - Topo 2014-06-27 15:41
@topo, i dug into napping's source code, if payload is set, they call json.Marshall on it, I'm not sure why it wasn't working for him - OneOfOne 2014-06-27 19:44


371

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))
}
2014-06-27 15:33
by OneOfOne
now it have panics on playground. May be you shold fix or update something - Altenrion 2017-01-04 16:02
@Altenrion it can't work on the playground, I just used it to paste the code, you can't open external connections from it - OneOfOne 2017-01-08 11:04
@Altenrion may this link help yo - Ardi Nusawan 2018-03-28 09:34
@Altenrion +1 for solid band name suggestion - Charlie Schliesser 2018-03-30 19:10
Just a warning, don't forget that by default the golang http client never times out, so for the real world, best to set something along the lines of client.Timeout = time.Second * 15svarlamov 2018-08-03 04:30
Can this be updated to gather / inspect all it's errors? This is (for me, at least) the top result on google for making post requests in Go, and it's a good answer, but I see a ton of example code that just ignores errors, and I think it encourages bad practice in newbies. Then again, if anyone regularly ignores errors, I suppose they'll learn why not to eventually, but why not encourage good practice to begin with - K. Rhoda 2018-10-17 20:20


62

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))
2016-11-05 09:25
by gaozhidf
I get this error : cannot use jsonValue (type []byte) as type io.Reader in argument to http.Post: []byte does not implement io.Reader (missing Read method)Mandar Vaze 2016-12-30 12:52
@MandarVaze i think you may get wrong io.Reader for http.Post, and bytes.NewBuffer() works well in my cod - gaozhidf 2017-01-02 08:03
I'm on go 1.7, if it matters. The code listed by @OneOfOne works (which also uses bytes.NewBuffer() but uses http.NewRequest instead of http.Post - Mandar Vaze 2017-01-04 07:11


11

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.

2014-11-09 05:13
by A-letubby
If GoRequest wraps net/http. Is it possible to add this to disable the Insecure certificate for TLS ? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }user1513388 2015-10-05 17:58
@user1513388 It's always a terrible idea to contribute code examples of skipping TLS verification in any scenario in any language... you accidentally perpetuate a lot copy/paste "workarounds" by neophytes who visit StackOverflow and don't understand the nature of why fixing TLS errors is crucial. Either fix your certificate import path (if using self-signed for testing, import those) or fix your machine's certificate chain, or find out why your server is presenting an invalid certificate that can't be verified by your client - Mike Atlas 2015-10-25 22:27
One thing I don't exactly like about this answer is the way it composes the JSON object, which is potentially exploitable via injection. A better way would be to compose an object and then transform it to JSON (with the proper escaping) - John White 2019-03-01 18:33


0

If you already have a struct.

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

// .....

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

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

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

defer res.Body.Close()
io.Copy(os.Stdout, res.Body)
2019-02-06 06:41
by Ninh Pham
Ads