如何在Go中的POST请求中发送JSON字符串


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的字符串?
Topo 2014年

1
如果要发送JSON,为什么要取消编组JSON?
JimB 2014年

一个小技巧,您可以将消息创建为结构或map [string] interface {}以添加所需的所有值,然后使用json.Marshall将地图或结构转换为json。
Topo 2014年

@topo,我研究了小睡的源代码,如果设置了有效负载,他们会调用json.Marshall它,我不确定为什么它对他不起作用。
OneOfOne 2014年

Answers:


500

我对打n并不熟悉,但是使用Golang的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))
}

1
现在它在操场上出现了恐慌。您可能要修复或更新某些内容吗?
Altenrion

9
@Altenrion它不能在操场上工作,我只是用它粘贴了代码,无法从中打开外部连接。
OneOfOne

8
@Altenrion +1表示实心的乐队名称。
查理·施利瑟

8
只是警告,不要忘记默认情况下golang http客户端永远不会超时,因此对于现实世界而言,最好按照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))

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.Readerhttp.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包之外,您还可以考虑使用包装在net / http上的GoRequest简化您的生活,而无需过多考虑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}, }
user1513388

46
@ user1513388在任何情况下以任何语言编写跳过TLS验证的代码示例,始终是一个可怕的主意...您偶然地使访问StackOverflow的新手不停地进行大量复制/粘贴“变通办法”,并且不了解为什么要修复 TLS错误至关重要。修复证书导入路径(如果使用自签名进行测试,请导入证书)或修复计算机的证书链,或者找出为什么服务器呈现的无效证书无法被客户端验证。
Mike Atlas

1
关于此答案,我不完全喜欢的一件事是它构成JSON对象的方式,可以通过注入来利用它。更好的方法是组合一个对象,然后将其转换为JSON(带有适当的转义)。
约翰·怀特

@JohnWhite我同意,感觉非常红宝石/ js /
pythonic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.