Wie sende ich eine JSON-Zeichenfolge in einer POST-Anforderung in Go?

244

Ich habe versucht, mit Apiary zu arbeiten, und eine universelle Vorlage erstellt, um JSON an den Mock-Server zu senden und diesen Code zu haben:

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

}

Dieser Code sendet JSON nicht richtig, aber ich weiß nicht warum. Die JSON-Zeichenfolge kann bei jedem Aufruf unterschiedlich sein. Ich kann das nicht verwenden Struct.

Ladislav Prskavec
quelle
Ich kenne einige der von Ihnen verwendeten Bibliotheken nicht, aber so wie ich es verstehe, versuchen Sie, eine Karte von Jsons zu senden. Warum schickst du nicht einfach den String mit dem JSON?
Topo
1
Warum entfernen Sie den JSON, wenn Sie JSON senden möchten?
JimB
Ein kleiner Tipp: Sie können Ihre Nachricht als Struktur- oder Map-Schnittstelle [string] {} erstellen, um alle gewünschten Werte hinzuzufügen, und dann mit json.Marshall die Map oder Struktur in einen json konvertieren.
Topo
@topo, ich habe mich in den Quellcode von napping vertieft. Wenn die Nutzlast eingestellt ist, rufen json.Marshallsie ihn auf. Ich bin mir nicht sicher, warum es bei ihm nicht funktioniert hat.
OneOfOne

Antworten:

500

Ich bin nicht mit Nickerchen vertraut, aber die Verwendung von Golangs net/httpPaket funktioniert gut ( Spielplatz ):

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
quelle
1
Jetzt hat es Panik auf dem Spielplatz. Vielleicht solltest du etwas reparieren oder aktualisieren?
Altenrion
9
@Altenrion es kann nicht auf dem Spielplatz funktionieren, ich habe es nur verwendet, um den Code einzufügen, Sie können keine externen Verbindungen von ihm öffnen.
OneOfOne
8
@Altenrion +1 für einen soliden Bandnamenvorschlag.
Charlie Schliesser
8
Nur eine Warnung, vergessen Sie nicht, dass der golang http-Client standardmäßig nie eine Zeitüberschreitung aufweist. Für die reale Welt ist es daher am besten, etwas in der Art vonclient.Timeout = time.Second * 15
svarlamov
1
Kann dies aktualisiert werden, um alle Fehler zu erfassen / zu überprüfen? Dies ist (zumindest für mich) das beste Ergebnis bei Google für Post-Anfragen in Go, und es ist eine gute Antwort, aber ich sehe eine Menge Beispielcode, der Fehler einfach ignoriert, und ich denke, dass dies zu schlechten Praktiken bei Neulingen führt. Andererseits, wenn jemand Fehler regelmäßig ignoriert, wird er wahrscheinlich lernen, warum er es nicht irgendwann tun sollte, aber warum er nicht zunächst zu bewährten Praktiken ermutigt?
K. Rhoda
103

Sie können nur verwenden post, um Ihren JSON zu posten.

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

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))
gaozhidf
quelle
3
Ich bekomme diesen Fehler: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 Ich glaube , Sie falsch kann io.Readerfür http.Postund bytes.NewBuffer () funktioniert gut in meinem Code
gaozhidf
1
Ich bin unterwegs 1.7, wenn es darauf ankommt. Der von @OneOfOne aufgelistete Code funktioniert (der auch verwendet, bytes.NewBuffer()aber http.NewRequestanstelle von verwendet http.Post)
Mandar Vaze
2
Laut golang.org/pkg/net/http/#Post sollte "der Anrufer nach dem resp.BodyLesen geschlossen werden. Wenn es sich bei dem bereitgestellten Text um einen handelt io.Closer, wird er nach der Anforderung geschlossen." Wie kann ich als Go-Neuling feststellen, ob der Körper ein Körper ist io.Closeroder mit anderen Worten, ob dieses Beispiel sicher ist?
Dennis
14

Wenn Sie bereits eine Struktur haben.

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)

Voller Kern .

Ninh Pham
quelle
12

Zusätzlich zum Standardpaket net / http können Sie meine GoRequest verwenden, die sich um net / http dreht und Ihnen das Leben erleichtert, ohne zu viel über json oder struct nachzudenken. Sie können aber auch beide in einer Anfrage kombinieren! (Weitere Details dazu finden Sie auf der Gorequest-Github-Seite.)

Am Ende wird Ihr Code also wie folgt aussehen:

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

Dies hängt davon ab, wie Sie erreichen möchten. Ich habe diese Bibliothek erstellt, weil ich das gleiche Problem mit Ihnen habe und Code möchte, der kürzer, mit json einfach zu verwenden und in meiner Codebasis und meinem Produktionssystem besser zu warten ist.

A-letubby
quelle
Wenn GoRequest net / http umschließt. Ist es möglich, dies hinzuzufügen, um das unsichere Zertifikat für TLS zu deaktivieren? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388
46
@ user1513388 Es ist immer eine schreckliche Idee Code Beispiele für das Überspringen TLS Überprüfung in jedem Szenario in jeder Sprache ... Sie versehentlich eine Menge Kopie verewigen / paste „Workarounds“ von Neophyten beitragen , die Stackoverflow und versteht nicht, welche Art von warum besuchen Befestigungs TLS-Fehler sind entscheidend. Korrigieren Sie entweder Ihren Zertifikatimportpfad (wenn Sie selbstsignierte zum Testen verwenden, importieren Sie diese) oder korrigieren Sie die Zertifikatkette Ihres Computers oder finden Sie heraus, warum Ihr Server ein ungültiges Zertifikat anzeigt, das von Ihrem Client nicht überprüft werden kann.
Mike Atlas
1
Eine Sache, die mir an dieser Antwort nicht gerade gefällt, ist die Art und Weise, wie das JSON-Objekt erstellt wird, das möglicherweise durch Injektion ausgenutzt werden kann. Ein besserer Weg wäre, ein Objekt zu komponieren und es dann in JSON umzuwandeln (mit dem richtigen Escapezeichen).
John White
@ JohnWhite Ich stimme zu, fühlt sich sehr rubin / js / pythonic
Rambatino