Jak wysłać ciąg JSON w żądaniu POST w Go

244

Próbowałem pracować z pasieką i stworzyłem uniwersalny szablon, aby wysłać JSON na próbny serwer i mieć ten kod:

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

}

Ten kod nie wysyła poprawnie JSON, ale nie wiem dlaczego. Ciąg JSON może być inny w każdym wywołaniu. Nie mogę tego użyć Struct.

Ladislav Prskavec
źródło
Nie znam niektórych bibliotek, których używasz, ale jak rozumiem, próbujesz wysłać mapę Jsons. Dlaczego po prostu nie wyślesz łańcucha za pomocą jsona?
Topo
1
dlaczego rozmawiasz z Jsonem, jeśli chcesz wysłać JSona?
JimB
Mała wskazówka: możesz utworzyć wiadomość jako interfejs struct lub map [string] {}, aby dodać wszystkie potrzebne wartości, a następnie użyć json.Marshall do konwersji mapy lub struct na json.
Topo
@ topo, wkopałem się w kod źródłowy drzemki, jeśli ustawiony jest ładunek, wywołują json.Marshallgo, nie jestem pewien, dlaczego to nie działało dla niego.
OneOfOne

Odpowiedzi:

502

Nie znam drzemki, ale korzystanie z net/httppakietu Golanga działa dobrze ( plac zabaw ):

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
źródło
1
teraz panika na placu zabaw. Czy możesz coś poprawić lub zaktualizować?
Altenrion
9
@Altenrion nie może działać na placu zabaw, użyłem go tylko do wklejenia kodu, nie można otwierać z niego zewnętrznych połączeń.
OneOfOne
8
@Altenrion +1 za sugestię stałej nazwy zespołu.
Charlie Schliesser
8
Tylko ostrzeżenie, nie zapominaj, że domyślnie klient http golang nigdy się nie kończy, więc w prawdziwym świecie najlepiej ustawić coś w styluclient.Timeout = time.Second * 15
svarlamov
1
Czy można to zaktualizować, aby zebrać / sprawdzić wszystkie błędy? Jest to (przynajmniej dla mnie) najlepszy wynik w Google do wysyłania próśb o posty w Go, i to dobra odpowiedź, ale widzę mnóstwo przykładowego kodu, który po prostu ignoruje błędy i myślę, że zachęca to do złych praktyk dla początkujących. Z drugiej strony, jeśli ktoś regularnie ignoruje błędy, przypuszczam, że dowiedzą się, dlaczego w końcu nie, ale dlaczego nie zachęcać do dobrych praktyk na początek?
K. Rhoda,
103

możesz po prostu użyć, postaby opublikować swój Json.

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

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))
gaozhidf
źródło
3
cannot use jsonValue (type []byte) as type io.Reader in argument to http.Post: []byte does not implement io.Reader (missing Read method)
Pojawia
@MandarVaze myślę, może pojawić się źle io.Readerdla http.Posti bytes.NewBuffer () działa dobrze w moim kodu
gaozhidf
1
Jestem w drodze 1.7, jeśli to ma znaczenie. Kod wymieniony przez @OneOfOne działa (który również używa, bytes.NewBuffer()ale używa http.NewRequestzamiast http.Post)
Mandar Vaze
2
Według golang.org/pkg/net/http/#Post : „ Osoba dzwoniąca powinna zamknąć się resp.Bodypo zakończeniu czytania. Jeśli podana treść jest io.Closer, to jest zamykana po otrzymaniu żądania”. Jak mogę stwierdzić, jako nowicjusz Go, czy ciało jest io.Closer, lub innymi słowy, czy ten przykład jest bezpieczny?
Dennis
14

Jeśli masz już strukturę.

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)

Pełna treść .

Ninh Pham
źródło
12

Oprócz standardowego pakietu net / http, możesz rozważyć użycie mojego GoRequest, który obejmuje net / http i ułatwia ci życie bez zbytniego zastanawiania się nad jsonem lub struct. Ale możesz też mieszać i dopasowywać oba z nich w jednym żądaniu! (więcej szczegółów na ten temat można znaleźć na stronie githubest w Gorequest)

Tak więc ostatecznie twój kod będzie wyglądał następująco:

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

To zależy od tego, jak chcesz osiągnąć. Zrobiłem tę bibliotekę, ponieważ mam ten sam problem z tobą i chcę, aby kod był krótszy, łatwy w użyciu z Jsonem i bardziej łatwy w utrzymaniu w mojej bazie kodu i systemie produkcyjnym.

A-letubby
źródło
Jeśli GoRequest otacza net / http. Czy można to dodać, aby wyłączyć certyfikat Niebezpieczny dla TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388,
46
@ user1513388 Zawsze strasznym pomysłem jest podawanie przykładów kodu pomijania weryfikacji TLS w dowolnym scenariuszu w dowolnym języku ... przypadkowo utrwalasz wiele „obejść” skopiowanych / wklejonych przez neofitów, którzy odwiedzają StackOverflow i nie rozumiesz natury, dlaczego to naprawia Błędy TLS są kluczowe. Popraw ścieżkę importu certyfikatu (jeśli używasz samopodpisanego do testowania, zaimportuj je) lub napraw łańcuch certyfikatów komputera lub dowiedz się, dlaczego serwer przedstawia nieprawidłowy certyfikat, którego klient nie może zweryfikować.
Mike Atlas,
1
Jedną rzeczą, która nie podoba mi się w tej odpowiedzi, jest sposób, w jaki komponuje obiekt JSON, który potencjalnie można wykorzystać przez wstrzyknięcie. Lepszym sposobem byłoby skomponowanie obiektu, a następnie przekształcenie go w JSON (z odpowiednim ucieczką).
John White
@JohnWhite Zgadzam się, czuje się bardzo rubinowy / js / pythonic
Rambatino