Bagaimana cara mengirim string JSON dalam permintaan POST di Go

244

Saya mencoba bekerja dengan Apiary dan membuat template universal untuk mengirim JSON ke server tiruan dan memiliki kode ini:

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

}

Kode ini tidak mengirim JSON dengan benar, tetapi saya tidak tahu mengapa. String JSON dapat berbeda di setiap panggilan. Saya tidak bisa menggunakan Structini.

Ladislav Prskavec
sumber
Saya tidak terbiasa dengan beberapa perpustakaan yang Anda gunakan, tetapi seperti yang saya mengerti, Anda mencoba mengirim peta Jsons. Mengapa kamu tidak mengirim string dengan json saja?
Topo
1
mengapa kamu membatalkan perintah json jika kamu ingin mengirim json?
JimB
Sedikit tip, Anda dapat membuat pesan sebagai antarmuka atau string [string] peta {} untuk menambahkan semua nilai yang Anda inginkan dan kemudian menggunakan json.Marshall untuk mengubah peta atau struct ke json.
Topo
@topo, saya menggali kode sumber tidur siang, jika payload diatur, mereka memanggilnya json.Marshall, saya tidak yakin mengapa itu tidak berhasil untuknya.
OneOfOne

Jawaban:

501

Saya tidak terbiasa dengan tidur siang, tetapi menggunakan net/httppaket Golang berfungsi dengan baik ( taman bermain ):

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))
}
OneOfOne
sumber
1
sekarang ada kepanikan di taman bermain. Mungkin Anda harus memperbaiki atau memperbarui sesuatu?
Altenrion
9
@Altenrion itu tidak bisa berfungsi di taman bermain, saya hanya menggunakannya untuk menempelkan kode, Anda tidak dapat membuka koneksi eksternal darinya.
OneOfOne
8
@Altenrion +1 untuk saran nama band yang solid.
Charlie Schliesser
8
Sekadar peringatan, jangan lupa bahwa secara default klien http golang tidak pernah mati, jadi untuk dunia nyata, sebaiknya atur sesuatu di sepanjang garisclient.Timeout = time.Second * 15
svarlamov
1
Bisakah ini diperbarui untuk mengumpulkan / memeriksa semua kesalahannya? Ini (bagi saya, setidaknya) hasil teratas di google untuk membuat permintaan posting di Go, dan ini adalah jawaban yang baik, tapi saya melihat banyak contoh kode yang mengabaikan kesalahan, dan saya pikir ini mendorong praktik buruk pada pemula. Kemudian lagi, jika ada yang secara teratur mengabaikan kesalahan, saya kira mereka akan belajar mengapa tidak pada akhirnya, tetapi mengapa tidak mendorong praktik yang baik untuk memulai?
K. Rhoda
103

Anda bisa menggunakannya postuntuk memposting json Anda.

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

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))
gaozhidf
sumber
3
Saya mendapatkan kesalahan ini: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
@MandarVaze saya pikir Anda mungkin mendapatkan salah io.Readeruntuk http.Post, dan bytes.NewBuffer () bekerja dengan baik dalam kode
gaozhidf
1
Saya sedang jalan 1.7, jika itu penting. Kode yang terdaftar oleh @OneOfOne berfungsi (yang juga menggunakan bytes.NewBuffer()tetapi menggunakan http.NewRequestbukan http.Post)
Mandar Vaze
2
Menurut golang.org/pkg/net/http/#Post , "Penelepon harus menutup resp.Bodyketika selesai membaca dari itu. Jika badan yang disediakan adalah io.Closer, itu ditutup setelah permintaan." Bagaimana saya bisa tahu, sebagai pemula, jika badan itu adalah io.Closer, atau dengan kata lain, apakah contoh ini aman?
Dennis
14

Jika Anda sudah memiliki 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)

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)

Inti penuh .

Ninh Pham
sumber
12

Selain paket net / http standar, Anda dapat mempertimbangkan untuk menggunakan GoRequest saya yang membungkus net / http dan membuat hidup Anda lebih mudah tanpa terlalu memikirkan json atau struct. Tetapi Anda juga dapat mencampur dan mencocokkan keduanya dalam satu permintaan! (Anda dapat melihat detail lebih lanjut tentang hal ini di halaman gorequest github)

Jadi, pada akhirnya kode Anda akan menjadi seperti ikuti:

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

Ini tergantung pada bagaimana Anda ingin mencapai. Saya membuat perpustakaan ini karena saya memiliki masalah yang sama dengan Anda dan saya ingin kode yang lebih pendek, mudah digunakan dengan json, dan lebih mudah dikelola dalam basis kode dan sistem produksi saya.

A-letubby
sumber
Jika GoRequest membungkus net / http. Apakah mungkin menambahkan ini untuk menonaktifkan sertifikat tidak aman untuk TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388
46
@ user1513388 Selalu merupakan ide yang buruk untuk berkontribusi contoh kode melewatkan verifikasi TLS dalam skenario apa pun dalam bahasa apa pun ... Anda secara tidak sengaja mengabadikan banyak salinan / tempel "pemecahan masalah" oleh orang baru yang mengunjungi StackOverflow dan tidak mengerti sifat mengapa memperbaiki Kesalahan TLS sangat penting. Perbaiki jalur impor sertifikat Anda (jika menggunakan yang ditandatangani sendiri untuk pengujian, impor itu) atau perbaiki rantai sertifikat mesin Anda, atau cari tahu mengapa server Anda menyajikan sertifikat yang tidak valid yang tidak dapat diverifikasi oleh klien Anda.
Mike Atlas
1
Satu hal yang saya tidak suka tentang jawaban ini adalah cara ia menyusun objek JSON, yang berpotensi dieksploitasi melalui injeksi. Cara yang lebih baik adalah dengan membuat objek dan kemudian mengubahnya menjadi JSON (dengan pelolosan yang tepat).
John White
@JohnWhite Saya setuju, terasa sangat ruby ​​/ js / pythonic
Rambatino