Problem:
In einem Go-Programm Bei einer Kontextfrist führt das Lesen eines Antworttexts mit ioutil.ReadAll() zum Fehler „Erwartete Frist überschritten“. Die Verwendung von json.NewDecoder(resp.Body).Decode() gibt jedoch stattdessen Null zurück.
Codebeispiel:
<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>
Antwort:
Im net/http-Paket können Puffer zur Verarbeitung von Anfragen verwendet werden. Folglich wird der eingehende Antworttext möglicherweise teilweise oder vollständig gelesen und gepuffert, bevor Sie versuchen, ihn zu lesen. Daher hindert Sie ein ablaufender Kontext möglicherweise nicht daran, die Lektüre des Textes abzuschließen. Genau das passiert in dieser Situation.
Zum besseren Verständnis ändern wir den Code, um einen Test-HTTP-Server zu erstellen, der die Antwort absichtlich verzögert:
<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>
Das geänderte Beispiel sendet einen JSON Objekt ähnlich der Antwort von ip.jsontest.com, überträgt jedoch nur die ersten 10 Bytes des Körpers, bevor er geleert wird. Anschließend wird die Übertragung für 6 Sekunden unterbrochen, sodass der Client eine Zeitüberschreitung erhält.
Wenn wir readDoesntFail() ausführen, beobachten wir das folgende Verhalten:
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
In diesem Szenario json.Decoder.Decode() versucht, aus der Verbindung zu lesen, da die Daten noch nicht gepuffert wurden. Sobald der Kontext abläuft, führen weitere Lesevorgänge aus der Verbindung zu einem Fehler wegen Fristüberschreitung. Im ursprünglichen Beispiel liest json.Decoder.Decode() jedoch bereits gepufferte Daten, wodurch der abgelaufene Kontext irrelevant wird.
Das obige ist der detaillierte Inhalt vonIgnoriert „json.NewDecoder().Decode()' Kontextfristen in Go-HTTP-Anfragen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!