Une requête GET
Chaque exemple de cette page démarre son propre serveur avec httptest.NewServer et l'appelle via une vraie connexion TCP sur localhost. Remplacez srv.URL par une vraie adresse comme https://api.example.com et le code client ne change pas.
Les trois règles visibles ici :
- Vérifiez
erren premier. Elle signale l'impossibilité d'obtenir la moindre réponse : erreurs DNS, connexions refusées, timeouts, problèmes TLS. defer resp.Body.Close()juste après la vérification d'erreur. Le body garde la connexion ouverte. Un body non fermé fait fuir la connexion.- Lisez ou décodez le body.
io.ReadAllpour du texte,json.NewDecoder(resp.Body).Decode(&v)pour du JSON.
Les codes de statut ne sont pas des erreurs
http.Get renvoie err == nil pour toute réponse envoyée par le serveur, y compris 404 et 500. Vous devez vérifier le statut vous-même :
io.LimitReader plafonne la quantité lue d'un body d'erreur, pour qu'un serveur défaillant ne vous fasse pas charger des mégaoctets dans un message d'erreur. Beaucoup d'API renvoient 201, 202 ou 204 en cas de succès ; vérifiez resp.StatusCode >= 200 && resp.StatusCode < 300 quand n'importe quel 2xx convient.
Timeouts : n'utilisez jamais le client par défaut pour du vrai trafic
http.Get, http.Post et http.DefaultClient n'ont aucun timeout global (le transport par défaut ne limite que l'établissement de la connexion et la négociation TLS). Un serveur qui accepte la connexion et ne répond jamais bloque votre goroutine indéfiniment. Créez votre propre client :
Client.Timeout couvre tout l'échange : la connexion, les redirections et la lecture du body. L'erreur qu'il renvoie est une *url.Error qui indique Timeout() == true, que errors.As trouve via l'interface net.Error. Créez un seul client et réutilisez-le partout ; un http.Client peut être utilisé en concurrence sans risque. Le pool de connexions vit dans son Transport, pas dans le Client : un nouveau http.Client{} par requête partage toujours http.DefaultTransport et son pool, mais un nouveau http.Transport par requête repart chaque fois d'un pool vide.
Requêtes avec un context et des en-têtes
http.NewRequestWithContext construit une requête que vous pouvez personnaliser avant de l'envoyer avec client.Do. Le context ajoute une échéance ou une annulation par requête en plus du timeout du client, et lie les appels sortants à la durée de vie d'une requête entrante dans un serveur.
Concaténer une saisie utilisateur dans une URL ("?q=" + q) casse avec &, # et les espaces. url.Values et sa méthode Encode font l'échappement, et trient les clés. Pour les segments de chemin, utilisez url.PathEscape.
POST JSON
client.Post(url, contentType, body) est un raccourci pour une requête avec cet en-tête Content-Type. Pour tout le reste (un en-tête d'authentification, PUT, PATCH, DELETE), utilisez http.NewRequestWithContext avec la méthode et client.Do. Pour les envois de formulaire, client.PostForm(url, url.Values{...}) encode les valeurs et définit application/x-www-form-urlencoded.
Réutilisation des connexions
Le client ne réutilise une connexion TCP que si vous lisez le body précédent jusqu'au bout et le fermez. Si vous arrêtez de lire plus tôt (par exemple après un statut d'erreur), videz-le avant de le fermer quand vous faites de nombreuses requêtes vers le même hôte :
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
Pour des bodies qui pourraient être énormes, videz plutôt via io.LimitReader, ou acceptez de perdre la connexion.
Redirections
Le client suit automatiquement jusqu'à 10 redirections, et resp.Request.URL vous indique où vous avez abouti. Pour vous arrêter à la première redirection, par exemple pour lire un en-tête Location, définissez CheckRedirect :
client := &http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // return the 3xx response itself
},
}
Erreurs courantes
- Utiliser
http.Getouhttp.DefaultClienten production. Pas de timeout. Créez unhttp.ClientavecTimeout. - Oublier
resp.Body.Close(). Les connexions fuient jusqu'à ce que les requêtes commencent à échouer. - Fermer le body avant de vérifier
err. Quanderrn'est pasnil,respvautniletresp.Body.Close()provoque un panic. - Considérer
err == nilcomme un succès. Vérifiezresp.StatusCode. - Créer un nouveau
http.Transportpar requête. Chaque transport a son propre pool de connexions, donc chaque requête ouvre une nouvelle connexion (et une nouvelle négociation TLS). Partagez un seul transport, généralement via un seulhttp.Clientpartagé. - Construire les chaînes de requête à la main. Utilisez
url.Values.
Questions fréquentes
Comment faire une requête GET en Go ?
resp, err := http.Get(url), vérifiez err, puis defer resp.Body.Close() et lisez le body avec io.ReadAll(resp.Body) ou décodez-le avec json.NewDecoder(resp.Body).Decode(&v). Vérifiez aussi resp.StatusCode : un 404 ou un 500 n'est pas une erreur pour http.Get.
Comment définir un timeout sur une requête HTTP en Go ?
Créez un client avec un timeout, client := &http.Client{Timeout: 10 * time.Second}, et utilisez-le à la place de http.Get ou de http.DefaultClient, qui n'ont pas de timeout global : un serveur qui accepte la connexion et ne répond jamais bloque l'appel indéfiniment. Pour une limite par requête, construisez la requête avec http.NewRequestWithContext et un context issu de context.WithTimeout.
Comment envoyer une requête POST avec un body JSON en Go ?
Sérialisez la valeur, puis envoyez-la : body, _ := json.Marshal(v) et http.Post(url, "application/json", bytes.NewReader(body)). Pour ajouter des en-têtes comme Authorization, construisez-la avec http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)), définissez req.Header.Set("Content-Type", "application/json"), et envoyez-la avec client.Do(req).
Pourquoi faut-il fermer resp.Body en Go ?
Le body est un flux sur une connexion ouverte. Le fermer rend la connexion au pool pour qu'elle soit réutilisée ; l'oublier fait fuir des connexions et des descripteurs de fichiers jusqu'à ce que le programme atteigne sa limite. Fermez-le même si vous ne le lisez pas, et lisez-le jusqu'au bout si vous voulez que la connexion soit réutilisée.