Как отправить строку 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для этого.

Ладислав Прскавец
источник
Я не знаком с некоторыми из используемых вами библиотек, но, насколько я понимаю, вы пытаетесь отправить карту Jsons. Почему бы тебе просто не отправить строку с JSON?
Топо
1
почему вы демонтируете JSON, если вы хотите отправить JSON?
ДжимБ,
Небольшой совет: вы можете создать свое сообщение как интерфейс struct или map [string] {}, чтобы добавить все нужные значения, а затем использовать json.Marshall для преобразования карты или структуры в json.
Топо
@topo, я копался в исходном коде дремоты, если полезная нагрузка установлена, они обращаются json.Marshallк ней, я не уверен, почему это не работает для него.
OneOfOne

Ответы:

502

Я не знаком с врасплох, но использование net/httpпакета Голанга прекрасно работает ( детская площадка ):

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
источник
1
сейчас паника на детской площадке. Может быть, вы должны исправить или обновить что-то?
Альтенрион
9
@ Altenrion это не может работать на детской площадке, я просто использовал его, чтобы вставить код, вы не можете открывать внешние соединения с него.
OneOfOne
8
@Altenrion +1 за внушительное название группы.
Чарли Шлиссер
8
Просто предупреждение, не забывайте, что по умолчанию http-клиент golang никогда не отключается, поэтому для реального мира лучше всего установить что-то вродеclient.Timeout = time.Second * 15
svarlamov
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))
gaozhidf
источник
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 и более удобным в моей кодовой базе и рабочей системе.

A-letubby
источник
Если GoRequest переносит net / http. Можно ли добавить это, чтобы отключить незащищенный сертификат для TLS? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388
46
@ user1513388 Всегда ужасно предлагать примеры кода пропуска проверки TLS в любом сценарии на любом языке ... вы случайно увековечиваете много копий / вставок "обходных путей" неофитами, которые посещают StackOverflow и не понимают природу того, почему исправление Ошибки TLS имеют решающее значение. Либо исправьте путь импорта сертификата (если вы используете самоподписанный для тестирования, импортируйте их), либо исправьте цепочку сертификатов вашего компьютера, либо выясните, почему ваш сервер представляет недействительный сертификат, который не может быть проверен вашим клиентом.
Майк Атлас
1
Что мне не нравится в этом ответе, так это то, как он создает объект JSON, который потенциально может использоваться посредством внедрения. Лучшим способом было бы создать объект и затем преобразовать его в JSON (с надлежащим экранированием).
Джон Уайт
@JohnWhite Я согласен, чувствует себя очень рубиновым / js /
pythonic