Як надсилати рядок JSON в POST-запиті в Go


244

Я спробував співпрацювати з Apiary і зробив універсальний шаблон для надсилання JSON на макет сервера та отримання цього коду:

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

}

Цей код не надсилає JSON належним чином, але я не знаю чому. Рядок JSON може бути різним у кожному дзвінку. Я не можу використовувати Structдля цього.


Я не знайомий з деякими бібліотеками, якими ви користуєтесь, але, як я це розумію, ви намагаєтесь надіслати карту Джосона. Чому ви просто не надішліть рядок з json?
Топо

1
чому ви скасуєте json, якщо хочете надіслати json?
JimB

Невелика порада, ви можете створити своє повідомлення у вигляді структури або карти [string] інтерфейсу {}, щоб додати всі потрібні значення, а потім використовувати json.Marshall для перетворення карти або структури в json.
Топо

@topo, я заглибився у вихідний код napping, якщо корисний вантаж встановлений, вони дзвонять json.Marshallйому, я не впевнений, чому це не працює для нього.
OneOfOne

Відповіді:


502

Я не знайомий з дрімотою, але використання net/httpпакету Golang чудово працює ( майданчик ):

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
тепер у нього паніка на дитячому майданчику. Можливо, вам щось виправлять чи оновлять?
Альтенріон

9
@Altenrion не може працювати на ігровому майданчику, я просто використовував його для вставки коду, ви не можете відкрити зовнішні з'єднання з ним.
OneOfOne

8
@Altenrion +1 - пропозиція назви твердого діапазону.
Чарлі Шліссер

8
Просто попередження, не забувайте, що за замовчуванням голанг http-клієнт ніколи не випромінюється, тому для реального світу найкраще встановити щось за client.Timeout = time.Second * 15
принципом

1
Чи можна це оновити, щоб зібрати / перевірити всі його помилки? Це (принаймні, для мене) найкращий результат в Google для надсилання запитів на пошту в Go, і це хороша відповідь, але я бачу тонну прикладного коду, який просто ігнорує помилки, і я думаю, що це заохочує погану практику у новачків. Знову ж таки, якщо хтось регулярно ігнорує помилки, я вважаю, що вони дізнаються, чому б не в кінцевому підсумку, але чому б не спонукати передової практики для початку?
К. Рода

103

ви можете просто використовувати postдля публікації свого json.

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

jsonValue, _ := json.Marshal(values)

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

3
Я отримую цю помилку: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 я думаю , що ви можете отримати не так io.Readerдля http.Postі bytes.NewBuffer () добре працює в моєму коді
gaozhidf

1
Я на ходу 1.7, якщо це має значення. Код, перелічений @OneOfOne, працює (який також використовує, bytes.NewBuffer()але використовує http.NewRequestзамість нього http.Post)
Mandar Vaze

2
За даними golang.org/pkg/net/http/#Post , " Телефонуючий користувач повинен закрити, resp.Bodyпрочитавши з нього. Якщо наданий орган є io.Closer, він закривається після запиту". Як я можу сказати, як новачок Go, якщо тіло є io.Closer, або іншими словами, якщо цей приклад безпечний?
Денніс

14

Якщо у вас вже є структура.

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)

Повна суть .


12

Окрім стандартного пакету net / http, Ви можете розглянути можливість використання мого GoRequest який обертається навколо net / http і полегшить ваше життя, не надто замислюючись про json або struct. Але ви також можете змішати і зіставити їх обох в одному запиті! (ви можете ознайомитися з детальніше про це на сторінці gorequest github)

Отже, зрештою, ваш код стане таким:

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

Це залежить від того, як ви хочете досягти. Я створив цю бібліотеку, оскільки у мене однакова проблема з вами, і я хочу, щоб код був коротшим, простішим у використанні з json та більш досконалим у моїй базі коду та виробничій системі.


Якщо GoRequest обгортає net / http. Чи можна додати це, щоб відключити сертифікат небезпеки для TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
користувач1513388

46
@ user1513388 Завжди страшна ідея надати приклади коду пропускання перевірки TLS в будь-якому сценарії будь-якою мовою ... Ви випадково увічните багато копіювання / вставлення "обхідних шляхів" неофітами, які відвідують StackOverflow і не розуміють природу, чому виправити Помилки TLS мають вирішальне значення. Або виправте шлях імпорту сертифікату (якщо ви використовуєте самопідписаний для тестування, імпортуйте їх) або виправте ланцюжок сертифікатів вашого пристрою або дізнайтеся, чому ваш сервер представляє недійсний сертифікат, який не може бути підтверджений вашим клієнтом.
Майк Атлас

1
Одне, що мені не подобається у цій відповіді, - це те, як він створює об'єкт JSON, який потенційно може бути використаний за допомогою ін'єкцій. Кращим способом було б скласти об’єкт, а потім перетворити його в JSON (при належному втечі).
Джон Уайт

@JohnWhite Я погоджуюся, відчуває себе дуже рубіно / js / pythonic
Rambatino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.