2017-11-11 51 views
1

我在Go中編寫了一個webhook,用於解析JSON有效內容。我正在嘗試記錄原始有效負載,然後立即對其進行解碼,但當我嘗試時卻失敗。如果我分別執行這些操作,它們都可以獨立運行。從golang網絡/ http庫中記錄和解碼repsonse主體

有人可以解釋爲什麼我不能使用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() 
} 
+0

「它失敗」究竟意味着什麼?對於json解碼,不要使用已經完全讀取的'r.Body',而是使用'body'。 – zerkms

回答

3

有人能解釋爲什麼我不能用ioutil.ReadAlljson.NewDecoder 在一起嗎?

請求正文是一個io.ReadCloser,它直接從網絡連接讀取字節,或多或少地讀取字節。 Body的內容默認不存儲在內存中。這就是爲什麼在你第一次閱讀身體的第一次,當你下次嘗試閱讀它時,你會得到EOF。

所以,如果你需要處理的請求主體不止一次,你自己必須將內容存儲到內存中,這是您已經在這樣做的:

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

然後,您可以重複使用body儘可能多的倍數,並且因爲您的身體內容可以作爲[]byte值使用,您可以使用json.Unmarshal而不是json.NewDecoder(...).Decode


這無關你的問題,但請不要忽略ioutil.ReadAll返回的錯誤。

此外,您可以刪除defer r.Body.Close()行,因爲您不必關閉服務器處理程序中的請求正文。 (重點煤礦

對於服務器請求請求主體始終是非零但會立即返回 EOF時沒有身體存在。 服務器將關閉 請求主體。 ServeHTTP處理程序不需要。

3

r.Body是爲了準確地讀取一次。 當您使用ioutil.ReadAll函數時,您將讀取正文中的所有數據。這就是爲什麼也依賴於r.Body的解碼器實際上沒有解碼的原因。

3

小的額外點約json.Decoderjson.Unmarshal:乍看起來兩者之間的唯一區別就是,前者在流並在[]byte後者運行,但他們實際上有不同的語義。

json.Unmarshal如果數據包含多個json對象,將返回一個錯誤。所以,例如,它會解析{},但它不會解析{}{}

json.Decoder解析每次調用一個完整的對象Decode,所以如果你給它{}{},它會解析這兩個對象,然後第三個調用將返回io.EOF和它的More方法將返回false

在一個普通的http主體中,你可能只需要一個對象,所以如果你不擔心一次加載所有的數據到內存,你會想要使用Unmarshal。您也可以使用Decoder並手動檢查是否只有一個對象,如果您願意的話。

+0

很高興知道'json.Unmarshal'和'json.Decoder'的細微差別 – Ravi