Ce face net/http.Client
http.Client trimite cereri și primește răspunsuri. http.Get și celelalte funcții folosesc http.DefaultClient, care nu are limită de timp. În programe reale creezi propriul client, o singură dată, și îl refolosești: client := &http.Client{Timeout: 10 * time.Second}. Clientul este sigur pentru folosire din mai multe goroutine și refolosește conexiunile.
client.Do(req) trimite o cerere construită cu http.NewRequest, iar client.Get(url) este scurtătura pentru GET. Timeout limitează toată cererea, inclusiv citirea corpului. CheckRedirect controlează redirecționările (returnează http.ErrUseLastResponse ca să nu le urmezi), iar Jar păstrează cookie-urile între cereri (net/http/cookiejar).
Ce returnează
Do și Get: răspunsul și o eroare. O eroare apare doar la probleme de rețea, timp depășit sau redirecționări refuzate, nu la coduri 4xx/5xx.
Exemple
Exemplul 1: Client cu limită de timp
package main
import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"time"
)
func main() {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/lent" {
select {
case <-time.After(time.Second):
case <-r.Context().Done():
return
}
}
fmt.Fprint(w, "răspuns pentru ", r.URL.Path)
}))
defer srv.Close()
client := &http.Client{Timeout: 100 * time.Millisecond}
for _, cale := range []string{"/rapid", "/lent"} {
resp, err := client.Get(srv.URL + cale)
if ne, ok := errors.AsType[net.Error](err); ok && ne.Timeout() {
fmt.Println(cale, "-> timpul a expirat")
continue
} else if err != nil {
fmt.Println(cale, "-> eroare:", err)
continue
}
corp, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(cale, "->", string(corp))
}
}
Rezultat
/rapid -> răspuns pentru /rapid
/lent -> timpul a expirat
Exemplul 2: Do cu cerere proprie și redirecționări oprite
CheckRedirect returnează http.ErrUseLastResponse, deci clientul nu urmează redirecționarea și primești chiar răspunsul 302.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/vechi" {
http.Redirect(w, r, "/nou", http.StatusFound)
return
}
fmt.Fprint(w, "pagina nouă")
}))
defer srv.Close()
normal := &http.Client{}
fara := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
for _, x := range []struct {
nume string
c *http.Client
}{{"normal", normal}, {"fără redirect", fara}} {
req, _ := http.NewRequest("GET", srv.URL+"/vechi", nil)
resp, err := x.c.Do(req)
if err != nil {
fmt.Println(err)
return
}
resp.Body.Close()
fmt.Printf("%-14s %d, Location=%q, adresa finală=%s\n", x.nume, resp.StatusCode, resp.Header.Get("Location"), resp.Request.URL.Path)
}
}
Rezultat
normal 200, Location="", adresa finală=/nou
fără redirect 302, Location="/nou", adresa finală=/vechi
Greșeli frecvente
- Creezi un
http.Clientnou la fiecare cerere: conexiunile nu mai sunt refolosite. Creează-l o dată (la nivel de pachet sau în structura serviciului). - Folosești
http.DefaultClientsauhttp.Getpentru servicii externe: fărăTimeout, o cerere blocată ține goroutine-ul pentru totdeauna. - Modifici
http.DefaultClient(de exempluTimeout): schimbarea afectează și bibliotecile care îl folosesc.
Comenzi înrudite
net/http.NewRequest: Construiește o cerere HTTP completă (orice metodă, antete, corp) care se trimite apoi cu Client.Do.net/http.Get: Trimite o cerere GET (sau HEAD, doar antete) la adresa dată și returnează răspunsul; corpul trebuie închis.net/http.Response: Răspunsul primit de client: codul de stare (StatusCode, Status), antetele (Header) și corpul (Body), care trebuie închis.net/http.Redirect: Trimite clientul la altă adresă (antetul Location) cu un cod 3xx: 301 mutat definitiv, 302/303 temporar.