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!