Comment envoyer une chaîne JSON dans une demande POST dans Go

244

J'ai essayé de travailler avec Apiary et j'ai créé un modèle universel pour envoyer JSON au faux serveur et avoir ce 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())

}

Ce code n'envoie pas correctement JSON, mais je ne sais pas pourquoi. La chaîne JSON peut être différente à chaque appel. Je ne peux pas l'utiliser Structpour ça.

Ladislav Prskavec
la source
Je ne connais pas certaines des bibliothèques que vous utilisez, mais si je comprends bien, vous essayez d'envoyer une carte de Jsons. Pourquoi n'envoyez-vous pas simplement la chaîne avec le json?
Topo
1
pourquoi démontez-vous le json si vous voulez envoyer du json?
JimB
Un petit conseil, vous pouvez créer votre message en tant qu'interface struct ou map [chaîne] {} pour ajouter toutes les valeurs souhaitées, puis utiliser json.Marshall pour convertir la map ou struct en json.
Topo
@topo, j'ai creusé dans le code source de la sieste, si la charge utile est définie, ils l'appellent json.Marshall, je ne sais pas pourquoi cela ne fonctionnait pas pour lui.
OneOfOne

Réponses:

502

Je ne suis pas familier avec la sieste, mais l'utilisation du net/httppackage de Golang fonctionne très bien ( terrain de jeu ):

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
la source
1
maintenant il panique sur le terrain de jeu. Peut-être que vous devez réparer ou mettre à jour quelque chose?
Altenrion
9
@Altenrion cela ne peut pas fonctionner sur le terrain de jeu, je l'ai juste utilisé pour coller le code, vous ne pouvez pas ouvrir de connexions externes depuis celui-ci.
OneOfOne
8
@Altenrion +1 pour une suggestion de nom de groupe solide.
Charlie Schliesser
8
Juste un avertissement, n'oubliez pas que par défaut, le client http golang n'expire jamais, donc pour le monde réel, mieux client.Timeout = time.Second * 15
vaut
1
Peut-il être mis à jour pour recueillir / inspecter toutes ses erreurs? C'est (pour moi, au moins) le meilleur résultat sur Google pour faire des demandes de publication dans Go, et c'est une bonne réponse, mais je vois une tonne d'exemple de code qui ignore simplement les erreurs, et je pense que cela encourage les mauvaises pratiques chez les débutants. Là encore, si quelqu'un ignore régulièrement les erreurs, je suppose qu'il apprendra pourquoi pas, mais pourquoi ne pas encourager les bonnes pratiques pour commencer?
K. Rhoda
103

vous pouvez simplement utiliser postpour publier votre json.

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

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))
gaozhidf
la source
3
Je reçois cette erreur: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 je pense que vous pouvez obtenir mal io.Readerpour http.Postet bytes.NewBuffer () fonctionne bien dans mon code
gaozhidf
1
Je suis en route 1.7, si c'est important. Le code répertorié par @OneOfOne fonctionne (qui utilise également bytes.NewBuffer()mais utilise à la http.NewRequestplace de http.Post)
Mandar Vaze
2
Selon golang.org/pkg/net/http/#Post , "l'appelant devrait fermer resp.Bodylorsqu'il a fini de le lire. Si le corps fourni est un io.Closer, il est fermé après la demande." Comment puis-je savoir, en tant que débutant Go, si le corps est un io.Closer, ou en d'autres termes, si cet exemple est sûr?
Dennis
14

Si vous avez déjà une structure.

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)

Essentiel plein .

Ninh Pham
la source
12

En plus du package standard net / http, vous pouvez envisager d'utiliser ma GoRequest qui entoure net / http et vous simplifie la vie sans trop penser à json ou struct. Mais vous pouvez également mélanger et assortir les deux en une seule demande! (vous pouvez voir plus de détails à ce sujet dans la page github de gorequest)

Donc, à la fin, votre code deviendra comme suit:

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

Cela dépend de la façon dont vous souhaitez y parvenir. J'ai créé cette bibliothèque parce que j'ai le même problème avec vous et je veux un code plus court, facile à utiliser avec json et plus facile à gérer dans ma base de code et mon système de production.

A-letubby
la source
Si GoRequest encapsule net / http. Est-il possible d'ajouter ceci pour désactiver le certificat non sécurisé pour TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388
46
@ user1513388 C'est toujours une idée terrible de contribuer à des exemples de code de sauter la vérification TLS dans n'importe quel scénario dans n'importe quelle langue ... vous perpétuez accidentellement beaucoup de "contournements" de copier / coller par des néophytes qui visitent StackOverflow et ne comprennent pas la nature de la raison pour laquelle la correction Les erreurs TLS sont cruciales. Soit corrigez votre chemin d'importation de certificat (si vous utilisez l'auto-signature pour les tests, importez-les), soit corrigez la chaîne de certificats de votre machine, ou découvrez pourquoi votre serveur présente un certificat non valide qui ne peut pas être vérifié par votre client.
Mike Atlas du
1
Une chose que je n'aime pas exactement dans cette réponse est la façon dont elle compose l'objet JSON, qui est potentiellement exploitable via l'injection. Une meilleure façon serait de composer un objet, puis de le transformer en JSON (avec l'échappement approprié).
John White
@JohnWhite Je suis d'accord, se sent très rubis / js / pythonic
Rambatino