HTTP/2 – l’API HTTP Client de Java 11
Java 11 propose l’API HTTP Client pour faciliter l’utilisation côté client du protocole HTTP.
Cette API doit remplacer la classe historique HttpURLConnection introduite dans le JDK 1.1. Cette classe possède de nombreux inconvénients notamment :
- son âge et des difficultés à la maintenir
- pas facile à utiliser
- ne fonctionne qu’en mode synchrone
L’API HTTP Client a été introduite dans Java 9 sous la forme d’un module incubator : elle était dans la package jdk.incubator.http contenu dans le module jdk.incubator.httpclient.
Elle a connu quelques évolutions pour maintenant être incluse en standard dans un module de Java 11.
Cet article fait partie de la série HTTP/2, contenant les articles suivants :
- HTTP/2 : introduction
- HTTP/2 : les détails
- HTTP/2 : les anciennes pratiques à éviter maintenant
- HTTP/2 : l’API HTTP Client de Java 11
- HTTP/2 : Push serveur
- HTTP/2 : Push serveur avec Java EE 8
Présentation de l’API HTTP Client
La nouvelle API HTTP Client propose un support des versions 1.1 et 2 du protocole HTTP ainsi que les WebSockets côté client.
L’API est fournie en standard dans Java 11 : son but n’est pas de fournir une implémentation complète des protocoles mais d’être un compromis entre fonctionnalités et consommation de ressources.
Cette API offre une certaine modernité en termes de conception et de mise en oeuvre :
- utilisation du design pattern builder
- utilisation de fabriques pour obtenir des instances des builder mais aussi d’implémentations de certaines interfaces pour des usages courants
- utilisation de l’API Flow (reactive streams) pour fournir les données du body d’une requête (Flow.Publisher) et consommer le body d’une réponse (Flow.Subscriber)
L’API est contenu dans le package java.net.http du module java.net.http. Elle contient plusieurs types (interfaces, classes, énumération, exceptions) dont les principaux sont :
Classe/Interface | Rôle |
HttpClient | Instance immuable qui permet l’envoi d’une requête et la réception de la réponse correspondante en mode synchrone ou asynchrone |
HttpClient.Builder | Builder pour configurer et obtenir une instance de type HttpClient |
HttpRequest | Instance immuable qui encapsule une requête (URI, verbe (GET, POST, PUT, DELETE, …), headers, …) |
HttpRequest.Builder | Builder pour configurer et obtenir une instance de type HttpRequest |
HttpRequest.BodyPublisher | Fournit le contenu du body à la requête si elle en a besoin (requêtes de type POST et PUT) à partir de différentes sources |
HttpRequest.BodyPublishers | Fabrique pour obtenir des instances de type BodyPublishers pour des usages courants (chaîne de caractères, fichier, …) |
HttpResponse | Encapsule la réponse obtenue à une requête envoyée (code statut, headers, body, …) |
HttpResponse.ResponseInfo | Encapsule le code statut et les headers de la réponse |
HttpResponse.BodySubscriber | Consomme les octets du body d’une réponse pour les convertir en objet Java (chaîne de caractères, fichier, …) |
HttpResponse.BodySubscribers | Fabrique pour obtenir des instances de type BodyHandler pour des usages courants |
HttpResponse.BodyHandler | Interface fonctionnelle qui permet de gérer une réponse à partir des informations d’une ResponseInfo. Elle renvoie un BodyPublisher pour traiter le body de la réponse |
HttpResponse.BodyHandlers | Propose des fabriques pour des BodyHandler courants |
La classe HttpClient
L’obtention d’une instance de type HttpClient peut se faire de deux manières :
- l’utilisation de la fabrique newHttpClient() de la classe HttpClient pour obtenir une instance avec la configuration par défaut
HttpClient client = HttpClient.newHttpClient();
- l’utilisation de l’interface HttpClient.Builder qui est un builder pour configurer l’instance obtenue. Une instance du Builder est obtenue en utilisant la fabrique newBuilder() de la classe HttpClient
HttpClient httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1) .build();
La classe HttpClient utilise par défaut la version 2 du protocole HTTP.
Il est possible de préciser un proxy grâce à la méthode proxy() en lui passant en paramètre une instance de type ProxySelector
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.proxy(ProxySelector.of(new InetSocketAddress("proxy.oxiane.com", 80)))
.build();
La classe HttpRequest
Elle encapsule une requête à envoyer à un serveur.
Une instance de type HttpRequest est obtenue en utilisant un builder de type HttpRequest.Builder. Une instance de l’interface HttpRequest.Builder est obtenue en utilisant la fabrique newBuilder() de la classe HttpRequest.
Plusieurs méthodes permettent de configurer les éléments de la requête : l’uri, le verbe HTTP, les headers, un timeout.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://www.oxiane.com/"))
.GET()
.build();
La méthode build() permet d’obtenir l’instance.
Pour les requêtes de type POST et PUT, il est possible de fournir le contenu du body en utilisant un BodyPublisher. Un BodyPublisher permet de fournir les données qui seront contenues dans le body à partir d’une source.
La classe BodyPublishers propose des fabriques pour obtenir des instances de type BodyPublisher pour des besoins standards
HttpRequest requetePost = HttpRequest.newBuilder()
.uri(URI.create("http://www.oxiane.com/api/formations"))
.setHeader("Content-Type", "application/json")
.POST(BodyPublishers.ofString("{"cle1":"valeur1", "cle2":"valeur2"}"))
.build();
L’envoi de la requête et la réception de la réponse
Ils peuvent se faire de deux manières :
- synchrone : l’envoi de la requête bloque les traitements jusqu’à la réception de la réponse
- asynchrone : la requête est envoyée et le traitement de la réponse est effectué par un CompletableFuture
L’envoi en mode synchrone
Il se fait en utilisant la méthode send() de la classe HttpClient. Elle attend en paramètres :
- la requête
- une instance de type HttpResponse.BodyHandler qui permet de traiter le body selon la réponse
La classe HttpBodyHandlers propose des fabriques pour obtenir des instances de BodyHandler pour des usages courants.
HttpResponse response;
try {
response = httpClient.send(requete, BodyHandlers.ofString());
System.out.println("Status : " + response.statusCode());
System.out.println("Headers : " + response.headers());
System.out.println("Body : " + response.body());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
L’envoi en mode asynchrone
Il se fait en utilisant la méthode sendAsync() de la classe HttpClient. Elle attend en paramètres :
- la requête
- une instance de type HttpResponse.BodyHandler qui permet de traiter le body selon la réponse
Elle renvoie un CompletableFuture qui permet de définir les traitements à exécuter à la réception de la réponse.
httpClient.sendAsync(requete, BodyHandlers.ofString()).thenAccept(response -> {
System.out.println("Status : " + response.statusCode());
System.out.println("Headers : " + response.headers());
System.out.println("Body : " + response.body());
});
Conclusion
La nouvelle API HTTP Client de Java 11 propose en standard des facilités pour envoyer des requêtes HTTP et traiter leurs réponses avec une API moderne. Le support de HTTP/2 et des WebSockets permet la mise en œuvre de clients sans avoir recours à des bibliothèques tierces.
Le prochain article de cette série détaille le fonctionnement du mode Push d’HTTP/2.