Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Adakah `json.NewDecoder().Decode()` Mengabaikan Tarikh Akhir Konteks dalam Permintaan HTTP Go?

Adakah `json.NewDecoder().Decode()` Mengabaikan Tarikh Akhir Konteks dalam Permintaan HTTP Go?

Mary-Kate Olsen
Mary-Kate Olsenasal
2024-10-29 04:41:29682semak imbas

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

Go json.NewDecoder().Decode() Abaikan Tarikh Akhir Konteks?

Masalah:

Dalam program Go dengan tarikh akhir konteks, membaca badan respons menggunakan ioutil.ReadAll() menyebabkan tarikh akhir yang dijangkakan melebihi ralat. Walau bagaimanapun, menggunakan json.NewDecoder(resp.Body).Decode() mengembalikan sifar.

Contoh Kod:

<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>

Jawapan:

Dalam pakej net/http, penimbal boleh digunakan untuk memproses permintaan. Akibatnya, badan respons masuk mungkin dibaca sebahagian atau keseluruhannya dan ditimbal sebelum percubaan anda untuk membacanya. Akibatnya, konteks yang tamat tempoh mungkin tidak menghalang anda daripada melengkapkan bacaan kandungan. Inilah yang sebenarnya berlaku dalam situasi ini.

Untuk lebih memahami, mari kita ubah kod untuk mencipta pelayan HTTP ujian yang sengaja menangguhkan respons:

<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>

Contoh yang diubah suai menghantar JSON objek serupa dengan tindak balas ip.jsontest.com, tetapi menghantar hanya 10 bait pertama badan sebelum mengepamnya. Ia kemudiannya menangguhkan penghantaran selama 6 saat, memberi peluang kepada pelanggan untuk tamat masa.

Apabila kami melaksanakan readDoesntFail(), kami melihat tingkah laku berikut:

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

Dalam senario ini, json.Decoder.Decode() cuba membaca daripada sambungan kerana data belum ditimbal lagi. Setelah konteks tamat tempoh, bacaan lanjut daripada sambungan menyebabkan tarikh akhir melebihi ralat. Walau bagaimanapun, dalam contoh asal, json.Decoder.Decode() sedang membaca data yang sudah ditimbal, menjadikan konteks tamat tempoh tidak relevan.

Atas ialah kandungan terperinci Adakah `json.NewDecoder().Decode()` Mengabaikan Tarikh Akhir Konteks dalam Permintaan HTTP Go?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn