問題:
在 Go 程式中如果有上下文截止日期,使用 ioutil.ReadAll() 讀取回應正文會導致超出預期截止日期錯誤。但是,使用 json.NewDecoder(resp.Body).Decode() 會傳回 nil。
程式碼範例:
<code class="go">package main import ( "context" "encoding/json" "fmt" "io/ioutil" "net/http" "time" ) var url string = "http://ip.jsontest.com/" func main() { readDoesntFail() readFails() } type IpResponse struct { Ip string } func readDoesntFail() { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { panic(err) } resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } ipResponse := new(IpResponse) time.Sleep(time.Second * 6) fmt.Println("before reading response body, context error is:", ctx.Err()) err = json.NewDecoder(resp.Body).Decode(ipResponse) if err != nil { panic(err) } fmt.Println("Expected panic but there was none") } func readFails() { ctx, _ := context.WithTimeout(context.Background(), time.Second*5) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { panic(err) } resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } time.Sleep(time.Second * 6) fmt.Println("before reading response body, context error is:", ctx.Err()) _, err = ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("received expected error", err) } }</code>
答案:
在net/http包中,緩衝區可以用來處理請求。因此,在您嘗試讀取傳入的回應正文之前,可能會部分或全部讀取並緩衝它。因此,過期的上下文可能不會妨礙您完成正文的閱讀。這正是在這種情況下發生的情況。
為了更好地理解,讓我們更改程式碼以建立一個有意推遲回應的測試HTTP 伺服器:
<code class="go">ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s := []byte(`{"ip":"12.34.56.78"}`) w.Write(s[:10]) if f, ok := w.(http.Flusher); ok { f.Flush() } time.Sleep(time.Second * 6) w.Write(s[10:]) })) defer ts.Close() url = ts.URL readDoesntFail() readFails()</code>
修改後的範例傳送一個JSON與ip.jsontest.com 的回應類似的對象,但在刷新之前僅傳輸主體的前10 個位元組。然後它會暫停傳輸 6 秒,給客戶端一個超時的機會。
當我們執行readDoesntFail() 時,我們觀察到以下行為:
before reading response body, context error is: context deadline exceeded panic: Get "http://127.0.0.1:38230": context deadline exceeded goroutine 1 [running]: main.readDoesntFail() /tmp/sandbox721114198/prog.go:46 +0x2b4 main.main() /tmp/sandbox721114198/prog.go:28 +0x93
在這種情況下, json.Decoder.Decode() 嘗試從連接讀取,因為數據尚未緩衝。一旦上下文過期,從連線中進一步讀取會導致超出截止日期錯誤。然而,在原始範例中, json.Decoder.Decode() 正在讀取已經緩衝的數據,使過期的上下文變得無關緊要。
以上是`json.NewDecoder().Decode()` 是否會忽略 Go HTTP 請求中的上下文截止日期?的詳細內容。更多資訊請關注PHP中文網其他相關文章!