W3docs

Java HttpClient

Effectuez des requêtes HTTP en Java moderne avec java.net.http.HttpClient, avec prise en charge du mode sync, async et HTTP/2.

java.net.http.HttpClient, standardisé en Java 11, est l'API HTTP moderne à privilégier dans tout nouveau code. Il remplace le verbeux HttpURLConnection par une conception immuable basée sur un constructeur : HTTP/2 par défaut, appels synchrones et asynchrones natifs, et gestion enfichable des corps de requête et de réponse. Trois types font le travail — HttpClient, HttpRequest et HttpResponse.

Ce chapitre couvre la création et la réutilisation d'un client, la construction de requêtes avec corps et en-têtes, les modes d'envoi synchrone et asynchrone, la façon dont les BodyHandlers convertissent une réponse dans le type souhaité, et les pièges qui guettent les nouveaux utilisateurs. Si vous débutez en réseau Java, commencez par l'introduction au réseau ; pour le bloc de construction asynchrone utilisé ici, consultez CompletableFuture.

Les trois types

HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)       // HTTP/2, falling back to 1.1
        .connectTimeout(Duration.ofSeconds(10))
        .followRedirects(HttpClient.Redirect.NORMAL)
        .build();

Un seul HttpClient est thread-safe et réutilisable — créez-en un et partagez-le dans toute l'application ; ne créez pas une instance par requête. À partir de lui, vous envoyez des objets HttpRequest et obtenez des objets HttpResponse.

Construction d'une requête

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/api"))
        .header("Accept", "application/json")
        .timeout(Duration.ofSeconds(5))
        .POST(HttpRequest.BodyPublishers.ofString("{\"x\":1}"))
        .build();

La méthode de verbe (GET(), POST(...), PUT(...), DELETE()) est choisie sur le constructeur. Un BodyPublisher fournit le corps de la requête — ofString, ofByteArray, ofFile ou noBody(). Les requêtes sont immuables une fois construites et peuvent être réutilisées.

Envoi : synchrone et asynchrone

// Blocking
HttpResponse<String> resp =
        client.send(request, HttpResponse.BodyHandlers.ofString());

// Non-blocking — returns a CompletableFuture
CompletableFuture<HttpResponse<String>> future =
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

Un BodyHandler décide comment le corps de la réponse est matérialisé : ofString(), ofByteArray(), ofFile(path), ofLines() (un Stream<String>), ou discarding(). sendAsync retourne un CompletableFuture, vous pouvez donc enchaîner .thenApply, .thenAccept et .exceptionally sans bloquer un thread.

Lecture de la réponse

HttpResponse<T> est un simple objet de valeur typé. Les méthodes les plus utilisées :

  • statusCode() — le statut HTTP sous forme d'int (ex. 200, 404). Il n'y a pas de méthode isSuccessful() ; vérifiez le code vous-même.
  • body() — le corps, déjà converti en T par le BodyHandler que vous avez fourni.
  • headers() — un HttpHeaders. Utilisez firstValue("Content-Type") (retourne un Optional<String>) ou allValues("Set-Cookie") pour les en-têtes répétés.
  • uri() — l'URI final, qui peut différer de l'URI de la requête après une redirection.

Les noms d'en-têtes sont comparés sans distinction de casse, donc firstValue("content-type") et firstValue("Content-Type") retournent la même valeur.

Exemple complet : GET synchrone, POST synchrone, et asynchrone

Ce programme crée un endpoint loopback qui signale la méthode HTTP reçue, puis l'interroge de trois façons avec un seul HttpClient partagé : un GET synchrone, un POST synchrone avec corps, et un GET asynchrone via un CompletableFuture.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • Un seul HttpClient a servi les trois requêtes. Le client est immuable et thread-safe, le bon modèle est donc de le construire une fois et de le partager partout ; créer un client par appel gaspille les pools de connexions et les sessions HTTP/2. Remarquez l'absence de disconnect() — le client gère les connexions pour vous.
  • Le verbe de la requête était défini sur le constructeur : .GET() pour la lecture et .POST(BodyPublishers.ofString("payload")) pour l'écriture. Le serveur a renvoyé en écho la méthode reçue (handled GET, handled POST), confirmant que le publisher a transmis le corps et défini le verbe.
  • HttpResponse est un objet typé avec statusCode() et body(). Comme un BodyHandlers.ofString() a été fourni, le corps est revenu déjà décodé en String — remplacez par ofByteArray, ofFile ou ofLines et le même appel produit des octets, un fichier sauvegardé, ou un flux de lignes.
  • L'appel asynchrone a retourné un CompletableFuture et s'est composé avec .thenApply sans bloquer un thread jusqu'à future.get(). C'est l'avantage structurel sur HttpURLConnection : la concurrence est intégrée, donc des centaines de requêtes en vol simultané ne nécessitent pas des centaines de threads bloqués.
  • L'ensemble du flux — client, requête, envois synchrone et asynchrone, réponses typées — n'a utilisé ni flux manuels ni piège de flux d'erreur. Par rapport à HttpURLConnection, HttpClient est plus court, plus sûr et plus puissant, c'est pourquoi il est le choix par défaut en Java 11+.

Pièges courants

  • Un code 4xx ou 5xx n'est pas une exception. Contrairement à certaines bibliothèques, HttpClient retourne la réponse normalement pour tout statut reçu ; seuls les échecs de transport (connexion refusée, timeout, DNS) lèvent une IOException. Inspectez toujours statusCode() — le corps d'une réponse d'erreur reste lisible.
  • Créez un seul client, partagez-le. HttpClient est immuable, thread-safe et possède son propre pool de connexions. En créer un par requête abandonne la réutilisation des connexions et les sessions HTTP/2. Il n'y a pas de close() à appeler avant Java 21 (et en Java 21+ il est AutoCloseable, mais un client partagé à longue durée de vie a rarement besoin d'être fermé).
  • connectTimeout et timeout sont différents. HttpClient.connectTimeout(...) limite la durée d'établissement de la connexion TCP ; HttpRequest.timeout(...) limite l'ensemble de la requête/réponse. Une requête qui expire complète le future exceptionnellement avec une HttpTimeoutException.
  • Un GET ne peut pas porter un corps. Appeler GET() avec un BodyPublisher n'est pas la façon dont l'API fonctionne — utilisez POST, PUT, ou la méthode générique method(name, publisher) pour les verbes qui envoient des données.
  • URI.create doit recevoir un URI absolu et bien formé. Les espaces et autres caractères non sûrs ne sont pas encodés pour vous ; encodez les paramètres de requête avant de construire l'URI.

Pratique

Pratique
Dans un service à fort trafic, un développeur écrit 'HttpClient.newHttpClient()' dans la méthode qui traite chaque requête entrante, créant un nouveau client par appel. Les réviseurs le signalent. Pourquoi ?
Dans un service à fort trafic, un développeur écrit 'HttpClient.newHttpClient()' dans la méthode qui traite chaque requête entrante, créant un nouveau client par appel. Les réviseurs le signalent. Pourquoi ?
Was this page helpful?