首頁  >  文章  >  後端開發  >  `json.NewDecoder().Decode()` 是否會忽略 Go HTTP 請求中的上下文截止日期?

`json.NewDecoder().Decode()` 是否會忽略 Go HTTP 請求中的上下文截止日期?

Mary-Kate Olsen
Mary-Kate Olsen原創
2024-10-29 04:41:29681瀏覽

 Does `json.NewDecoder().Decode()` Ignore Context Deadlines in Go HTTP Requests?

Go json.NewDecoder().Decode() 忽略上下文截止日期?

問題:

在 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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn