我在Go中编写了一个解析JSON有效负载的webhook。我尝试记录原始有效负载,然后立即对其进行解码,但是当我尝试时它失败了。如果我单独执行操作,它们都可以独立工作。
有人可以解释为什么我不能同时使用ioutil.ReadAll
和json.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()
}
答案 0 :(得分:4)
有人可以解释为什么我无法使用
ioutil.ReadAll
和json.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.Decoder
和json.Unmarshal
的小额外点:乍一看,两者之间的唯一区别就是前者在流上操作而后者在[]byte
上操作,但它们实际上有不同的语义。
json.Unmarshal
将返回错误。因此,例如,它会解析{}
,但不会解析{}{}
。
json.Decoder
每次调用Decode
时会解析一个完整的对象,因此,如果您给它{}{}
,它将解析这两个对象,然后第三个调用将返回io.EOF
并且它的More
方法将返回false
。
在普通的http正文中,您可能只需要一个对象,因此如果您不担心一次将所有数据加载到内存中,则需要使用Unmarshal
。您也可以使用Decoder
并手动检查是否只有一个对象。