从golang net / http库

时间:2017-11-11 08:04:31

标签: go

我在Go中编写了一个解析JSON有效负载的webhook。我尝试记录原始有效负载,然后立即对其进行解码,但是当我尝试时它失败了。如果我单独执行操作,它们都可以独立工作。

有人可以解释为什么我不能同时使用ioutil.ReadAlljson.NewDecoder吗?

func webhook(w http.ResponseWriter, r *http.Request) {
    body, _ := ioutil.ReadAll(r.Body)
    log.Printf("incoming message - %s", body)

    var p payload
    decoder := json.NewDecoder(r.Body)
    err := decoder.Decode(&p)
    if err != nil {
        // Returns EOF
        log.Printf("invalid payload - %s", err)
    }

    defer r.Body.Close()
}

3 个答案:

答案 0 :(得分:4)

  

有人可以解释为什么我无法使用ioutil.ReadAlljson.NewDecoder   一起?

请求正文是io.ReadCloser,它可以直接从网络连接读取或多或少的字节。默认情况下,Body的内容不会存储在内存中。这就是为什么在你下次尝试阅读它之后第一次读到Body时,你会得到EOF。

因此,如果您需要多次处理请求Body,您自己必须将内容存储到内存中,这就是您正在使用的内容:

body, _ := ioutil.ReadAll(r.Body)

然后,您可以根据需要多次重复使用body,并且由于您可以将Body内容作为[]byte值使用,因此您可以使用json.Unmarshal代替{{ 1}}。

这与您的问题无关,但请不要忽略json.NewDecoder(...).Decode返回的错误。

此外,您可以删除ioutil.ReadAll行,因为您不必关闭服务器处理程序中的请求正文。 (强调我的

  

对于服务器请求,Request Body始终为非零但将返回   当没有身体时立即EOF。 服务器将关闭   请求机构。 ServeHTTP处理程序不需要。

答案 1 :(得分:3)

r.Body只能读一次。 当您使用ioutil.ReadAll函数时,您会读取正文中的所有数据。这就是为什么依赖于r.Body的解码器实际上无需解码。

答案 2 :(得分:3)

关于json.Decoderjson.Unmarshal的小额外点:乍一看,两者之间的唯一区别就是前者在流上操作而后者在[]byte上操作,但它们实际上有不同的语义。

如果数据包含多个json对象,

json.Unmarshal将返回错误。因此,例如,它会解析{},但不会解析{}{}

json.Decoder每次调用Decode时会解析一个完整的对象,因此,如果您给它{}{},它将解析这两个对象,然后第三个调用将返回io.EOF并且它的More方法将返回false

在普通的http正文中,您可能只需要一个对象,因此如果您不担心一次将所有数据加载到内存中,则需要使用Unmarshal。您也可以使用Decoder并手动检查是否只有一个对象。