Come faccio a inviare una stringa JSON in una richiesta POST in Go


244

Ho provato a lavorare con Apiary e ho creato un modello universale per inviare JSON a un server simulato e avere questo codice:

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

}

Questo codice non invia correttamente JSON, ma non so perché. La stringa JSON può essere diversa in ogni chiamata. Non posso usare Structper questo.


Non ho familiarità con alcune delle librerie che usi, ma a quanto ho capito, stai cercando di inviare una mappa di Jsons. Perché non invii semplicemente la stringa con il json?
Topo,

1
perché stai smantellando il json se vuoi inviare il json?
JimB,

Un piccolo suggerimento, è possibile creare il messaggio come interfaccia struct o map [string] {} per aggiungere tutti i valori desiderati e quindi utilizzare json.Marshall per convertire la mappa o struct in json.
Topo,

@topo, ho scavato nel codice sorgente del pisolino, se è impostato il payload, lo chiamano json.Marshall, non sono sicuro del perché non funzionasse per lui.
OneOfOne,

Risposte:


502

Non ho familiarità con il pisolino, ma l'uso del net/httppacchetto di Golang funziona bene ( parco giochi ):

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

1
ora ha il panico nel parco giochi. Potresti riparare o aggiornare qualcosa?
Altenrion,

9
@Altenrion non può funzionare nel parco giochi, l'ho appena usato per incollare il codice, non è possibile aprire connessioni esterne da esso.
OneOfOne

8
@Altenrion +1 per il suggerimento del nome di una band solida.
Charlie Schliesser,

8
Solo un avvertimento, non dimenticare che per impostazione predefinita il client http golang non scade mai, quindi per il mondo reale, è meglio impostare qualcosa sulla client.Timeout = time.Second * 15
falsariga

1
Questo può essere aggiornato per raccogliere / ispezionare tutti i suoi errori? Questo è (almeno per me) il miglior risultato su Google per fare richieste di post su Go, ed è una buona risposta, ma vedo un sacco di codice di esempio che ignora gli errori e penso che incoraggi le cattive pratiche nei neofiti. Poi di nuovo, se qualcuno ignora regolarmente gli errori, suppongo che impareranno perché non alla fine, ma perché non incoraggiare le buone pratiche per cominciare?
K. Rhoda,

103

puoi semplicemente usare postper pubblicare il tuo json.

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

jsonValue, _ := json.Marshal(values)

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

3
Ottengo questo errore: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 il

@MandarVaze penso che si può ottenere sbagliato io.Readerper http.Post, e bytes.NewBuffer () funziona bene nel mio codice
gaozhidf

1
Sto andando all'1.7, se è importante. Il codice elencato da @OneOfOne funziona (che utilizza anche bytes.NewBuffer()ma utilizza http.NewRequestinvece di http.Post)
Mandar Vaze

2
Secondo golang.org/pkg/net/http/#Post , "Il chiamante dovrebbe chiudersi resp.Bodydopo aver letto da esso. Se il corpo fornito è un io.Closer, viene chiuso dopo la richiesta." Come posso dire, come novizio Go, se il corpo è un io.Closer, o in altre parole, se questo esempio è sicuro?
Dennis

14

Se hai già una struttura.

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)

Gist completo .


12

Oltre al pacchetto standard net / http, puoi prendere in considerazione l'uso del mio GoRequest che avvolge net / http e semplifica la vita senza pensare troppo a json o struct. Ma puoi anche mescolare e abbinare entrambi in una richiesta! (puoi vedere maggiori dettagli a riguardo nella pagina github di gorequest)

Quindi, alla fine il tuo codice diventerà come segue:

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

Questo dipende da come vuoi raggiungere. Ho creato questa libreria perché ho lo stesso problema con te e voglio un codice più breve, facile da usare con json e più gestibile nel mio codice e nel mio sistema di produzione.


Se GoRequest esegue il wrapping net / http. È possibile aggiungere questo per disabilitare il certificato Insecure per TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388,

46
@ user1513388 È sempre una terribile idea fornire esempi di codice per saltare la verifica TLS in qualsiasi scenario in qualsiasi lingua ... perpetuamente si copiano / incollano molto "soluzioni" dai neofiti che visitano StackOverflow e non capiscono la natura del perché la correzione Gli errori TLS sono cruciali. Correggi il percorso di importazione del certificato (se usi l'autofirmazione per i test, importa quelli) o correggi la catena di certificati del tuo computer o scopri perché il tuo server presenta un certificato non valido che non può essere verificato dal tuo client.
Mike Atlas,

1
Una cosa che non mi piace esattamente di questa risposta è il modo in cui compone l'oggetto JSON, che è potenzialmente sfruttabile tramite l'iniezione. Un modo migliore sarebbe quello di comporre un oggetto e poi trasformarlo in JSON (con la corretta escape).
John White

@JohnWhite Sono d'accordo, mi sento molto rubino / js / pythonic
Rambatino
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.