Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Jakarta EE
  3. Les services web REST
Extrait - Jakarta EE Développez des applications web en Java
Extraits du livre
Jakarta EE Développez des applications web en Java Revenir à la page d'achat du livre

Les services web REST

Introduction

La plateforme Jakarta EE est un ensemble important de technologies. Toutes répondent à différents besoins. Parmi ces technologies, certaines sont actuellement très populaires dans le monde du développement web.

Il s’agit de :

  • La technologie Jakarta RESTful Web Services (anciennement JAX-RS) dans sa version 3.0. Elle met à disposition une API Java pour réaliser des Services Web REST. Le terme JAX-RS sera utilisé dans la suite de ce chapitre pour faire référence à cette technologie. 

  • La technologie Jakarta WebSocket dans sa version 2.0 mettant à disposition une API Java pour mettre en place une communication web basée sur le protocole WebSocket. Cette technologie sera présentée dans le prochain chapitre de cet ouvrage. 

Présentation des services web REST

1. Les principes

Le Web a pour principal objectif de permettre l’accès à des ressources identifiées à l’aide d’une URL (Uniform Resource Locator).

Ces ressources sont très souvent représentées sous forme d’une page web à l’aide du langage HTML. Mais ces ressources peuvent aussi être retournées sous d’autres formats ou types de médias (XML, PDF, images...). L’en-tête de requête Accept définit le type de média attendu par le client et l’en-tête de réponse Content-Type définit le type de média effectivement obtenu. Pour plus d’informations, veuillez vous référer à la section Les types de médias du chapitre Introduction à Jakarta EE.

L’application utilisée pour visualiser ces ressources (très souvent un navigateur) change d’état avec la représentation de la ressource transférée. L’acronyme REST signifie REpresentational State Transfer. C’est un style d’architecture centrée sur les ressources. 

La lecture d’informations comme les sports praticables au sein du complexe sportif se fait naturellement au travers d’une URL en utilisant le protocole HTTP et la méthode GET :

http://www.fairedusport.com/Sports 
http://www.fairedusport.com/Sports/14 

Le premier exemple permet d’obtenir la liste des sports et le deuxième exemple permet d’obtenir le sport identifié...

La mise en place de l’environnement de développement

1. Le choix d’une implémentation

La technologie JAX-RS est disponible au travers de plusieurs implémentations parmi lesquelles nous retrouvons les suivantes :

Cette liste n’est pas exhaustive. L’implémentation Apache CXF est aussi largement utilisée mais elle n’est actuellement pas compatible avec la version 3.0 de JAX-RS. L’ouvrage n’a pas vocation à faire le choix d’une implémentation plutôt qu’une autre. Il va uniquement décrire le fonctionnement de la technologie JAX-RS. Tous les exemples fonctionneront sur ces deux implémentations sauf s’il est fait mention contraire.

2. La mise en place du projet et des dépendances

L’objectif de ce chapitre est d’appréhender le fonctionnement des services web REST au travers d’un projet exemple nommé Projet_REST.

 Commencez par créer un projet de type Gradle Project avec les mêmes caractéristiques que dans le premier chapitre.

 Complétez l’arborescence de répertoires...

La spécification JAX-RS

1. Introduction

La présentation n’a pas pour objectif d’être exhaustive. Elle permet cependant d’aborder les points essentiels à comprendre pour débuter un projet avec la spécification JAX-RS.

2. Le fonctionnement général

La spécification JAX-RS décrit la manière d’exposer des services web REST. Depuis la version 2.0, il existe aussi la description d’une API cliente. Avec cette API, il sera ainsi plus aisé de créer une application cliente écrite en Java nécessitant l’appel de services web REST. Une section lui est accordée (L’API cliente).

Revenons à l’exposition des services web REST sur le serveur. Lorsqu’une requête visant une ressource arrive sur le serveur, celle-ci va suivre un ensemble d’étapes avant d’obtenir une réponse pour le client. Voici le schéma que vous pouvez retrouver en annexe de la spécification officielle :

images/06EP03N.png

Ce schéma peut paraître compliqué de prime abord mais il nous donne une vue globale du chemin parcouru :

  • La requête (Request) arrive sur le serveur exposant les services web REST. La requête est alors prise en charge par l’implémentation JAX-RS choisie.

  • Les traitements représentés par la PreMatchContainerRequest Chain correspondent...

La configuration de l’application

Tomcat 10 est un conteneur de servlet 5.0. À ce titre, il implémente le mécanisme de pluggabilité permettant au démarrage de découvrir les éléments à charger comme l’implémentation JAX-RS choisie sans que le développeur ait besoin de configurer quoi que ce soit. Il suffit que la librairie (ici, l’implémentation JAX-RS) fournisse une classe dérivant de la classe ServletContainerInitializer. Cette classe est le point d’entrée pour charger et déclencher la configuration de l’implémentation choisie. Pour plus de précisions sur le fonctionnement, vous pouvez lire le chapitre Annotations and pluggability de la spécification Java Servlet Specification dans sa version 5.0.

Maintenant que ce problème est réglé, il faut configurer a minima l’implémentation JAX-RS. Le paramétrage incontournable est d’associer une URL racine permettant à toutes les URL commençant par celle-ci d’être prise en charge par l’implémentation choisie. Pour cela, le développeur a juste à créer une classe qui dérive de la classe jakarta.ws.rs.core.Application. Cette classe sera trouvée et utilisée dans la phase de configuration de l’implémentation JAX-RS choisie.

Voici un exemple...

L’exposition des ressources

1. Présentation

Pour manipuler une ressource, l’application cliente a besoin de connaître son URL. À partir de cette URL, l’application va pouvoir effectuer une requête HTTP avec la méthode de requête souhaitée (typiquement GET, POST, PUT ou DELETE). Cette requête va déclencher l’appel de la méthode ressource de la classe ressource que le développeur a écrit pour répondre à la requête.

Par défaut, une nouvelle instance de cette classe est créée pour chaque requête vers cette ressource. Le constructeur est d’abord appelé, ensuite les éventuelles dépendances de requête sont injectées avant d’appeler la méthode ressource appropriée.

2. La classe ressource et ses méthodes

La classe ressource est un POJO (Plain Old Java Object - bon vieil objet Java). Cette classe permet de définir une ressource accessible au travers d’une URL. Cette classe doit, au minimum, avoir une méthode annotée avec un désignateur de méthode de requête (@GET, @POST, @PUT, @DELETE, @HEAD, @OPTION) présent dans le package jakarta.ws.rs.

La classe ou la méthode doit aussi être annotée avec @Path pour définir l’URL contextuelle. Ainsi l’URL d’accès est la concaténation de l’URL racine de l’application (défini par @ApplicationPath) et de l’URL contextuelle (défini par @Path).

Voici un exemple d’une classe ressource basique nommée fr.editions_eni.jakartaee.jaxrs.services.GestionSports :

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
 
@Path("/Sports") 
public class GestionSports { 
 
 
    @GET 
    public String getSports() 
    { 
        return "Tennis,Squash,Padel,Badminton"; 
    } 
} 

Cette classe ressource permet de répondre à une requête sur l’URL /REST/Sports avec la méthode GET. Sur l’environnement de développement, l’URL complète est http://localhost:8080/Projet_REST/REST/Sports.

L’exécution de cette requête sur le navigateur donne le résultat...

La validation des données

La validation des données permet de s’assurer que celles-ci vérifient certaines contraintes prédéfinies. C’est un vaste sujet traité au travers la technologie nommée Bean Validation. Ce sujet a déjà été abordé dans le chapitre traitant de JSF (section La validation avec la spécification Bean Validation). La présente section permet d’avoir un aperçu de son utilisation dans le cadre des services web REST.

1. Les annotations

Tous les attributs des classes ressources et les paramètres des méthodes ressources définis avec les annotations décrites précédemment peuvent être soumis à validation. Pour cela, un ensemble d’annotations élémentaires présentes dans le package jakarta.validation.constraints sont disponibles.

Parmi ces annotations, il y a :

  • @NotNull pour définir un élément obligatoire,

  • @Min pour définir la valeur entière minimale d’un numérique,

  • @Max pour définir la valeur entière maximale d’un numérique,

  • @Size pour définir la taille minimale et maximale d’une chaîne de caractères,

  • @Pattern pour définir une expression régulière à respecter.

  • Il est aussi possible de créer sa propre contrainte. Il faut alors créer une annotation associée à une classe implémentant l’interface jakarta.validation....

Les providers

1. Présentation

L’implémentation JAX-RS utilisée peut être enrichie de classes dites provider. Ces classes permettent d’enrichir le comportement de l’implémentation pour le traitement des requêtes.

Ces classes doivent implémenter une ou plusieurs interfaces de la spécification JAX-RS et doivent être annotées avec @Provider pour être découvertes automatiquement par l’implémentation utilisée au démarrage.

Par défaut, une seule instance de ces classes est créée par application. Un constructeur public est obligatoire et peut contenir des paramètres annotés avec @Context. Par contre, il n’est bien sûr pas possible d’injecter via cette annotation des informations spécifiques à une requête puisque par défaut il n’y aura qu’une seule instance.

2. Les entity providers

Présentation

Les entity providers permettent de faire l’association entre les types Java et leurs représentations dans le corps des requêtes et des réponses. Les entity providers sont les MessageBodyReader et les MessageBodyWriter mentionnés dans les sections précédentes. Ces deux types sont des interfaces génériques. Elles doivent être implémentées par des classes pour être utilisées.

Lorsqu’une requête arrive sur le serveur et avant que la méthode ressource ne soit exécutée, un MessageBodyReader est utilisé pour lire le contenu du corps de la requête. 

Pour renvoyer une réponse à l’utilisateur, il faut écrire un contenu dans le corps de la réponse. C’est le rôle du MessageBodyWriter.

Jusqu’à présent, les exemples se sont cantonnés à des méthodes ressources retournant une chaîne de caractères. La raison est simple, les implémentations JAX-RS doivent embarquer des entity providers pour un certain nombre de types Java (comme java.lang.String) pour certains médias. Voici la liste des types Java devant être supportés :

  • Pour tous les types de médias (*/*) :

  • byte[]

  • java.lang.String

  • java.io.InputStream

  • java.io.Reader

  • java.io.File

  • javax.activation.DataSource

  • StreamingOutput, seulement...

Les filtres et les intercepteurs

Les filtres et les intercepteurs permettent d’ajouter des traitements sur les requêtes. Ces traitements peuvent être :

  • L’écriture de logs,

  • La gestion de l’authentification,

  • La gestion de la compression/décompression du contenu de la requête,

  • Etc.

Les filtres permettent de mettre en place un traitement quelle que soit l’URL demandée. Les intercepteurs permettent de mettre en place un traitement si un MessageBodyReader ou un MessageBodyWriter est utilisé. Veuillez vous référer à la section La spécification JAX-RS - Le fonctionnement général pour visualiser la position de ces étapes dans le traitement global d’une requête.

1. Les filtres

Introduction

Les filtres sont des classes qui implémentent l’interface jakarta.ws.rs.container.ContainerRequestFilter ou l’interface jakarta.ws.rs.container.ContainerResponseFilter. La première interface permet de mettre en place un traitement avant l’exécution de la méthode ressource, la deuxième interface permet de mettre en place un traitement après l’exécution de la méthode ressource.

Voici le code de ces deux interfaces :

public interface ContainerRequestFilter  
{ 
    void filter(ContainerRequestContext requestContext) throws IOException; 
} 
public interface ContainerResponseFilter 
{ 
    void filter(ContainerRequestContext requestContext, 
    ContainerResponseContext responseContext) throws IOException; 
} 

Elles décrivent, toutes les deux, une méthode filter(). C’est cette méthode qui sera appelée dans la chaîne de traitement. La signature est différente. La première ne prend qu’un paramètre de type ContainerRequestContext alors que la seconde prend un paramètre complémentaire de type ContainerResponseContext.

Ces paramètres font que le filtre peut accéder aux informations spécifiques de la requête. Le filtre peut par exemple lire les en-têtes de requête (et/ou de réponse) et en modifier si besoin. Le filtre peut éventuellement mettre le traitement de la requête en échec en appelant la méthode abortWith(Response) présente...

Complément sur le déploiement

La recherche des classes providers à prendre en charge par l’implémentation choisie pour répondre aux différentes requêtes se fait en parcourant l’ensemble des classes accessibles pour savoir si elles sont annotées avec @Provider. Ces classes peuvent être dans le répertoire WEB-INF/classes du projet mais peuvent être dans une bibliothèque référencée dans le répertoire WEB-INF/lib ou dans un autre répertoire du classpath. Ce mécanisme est très intéressant en phase de développement. En production, il est plus judicieux de mettre en place un mécanisme plus directif.

Pour cela, il est possible d’enrichir la classe ConfigurationApplicationREST en surchargeant les méthodes getClasses() et getSingletons() de la classe mère.

Par défaut, la méthode getClasses() retourne une liste vide indiquant à l’implémentation choisie de faire la recherche des classes par elle-même. Il est possible de changer le comportement de cette méthode pour qu’elle retourne explicitement les classes à utiliser. Dans ce cas, l’implémentation choisie ne se basera que sur cette liste de classes pour répondre aux requêtes.

Il peut être nécessaire, dans certains cas, de ne vouloir qu’une seule...

L’API cliente

1. Introduction

La spécification JAX-RS propose une API cliente. Cette API permet à des applications Java d’interroger des services web REST. Les composants principaux de cette API se trouvent dans le package jakarta.ws.rs.client. Les interfaces et les classes de ce package définissent les mécanismes pour créer une requête à destination d’un service web REST et pour traiter la réponse obtenue.

2. Le fonctionnement général

Voici le schéma que vous pouvez retrouver en annexe de la spécification officielle :

images/06EP24N.png

Comme vous pouvez l’observer, le fonctionnement côté client ressemble beaucoup dans les étapes au fonctionnement côté serveur.

Il est possible de mettre en place des filtres sur la requête (avant son envoi) et sur la réponse (après sa réception) sur les étapes nommées ClientRequest Chain et ClientResponse Chain. Le fonctionnement est identique aux mécanismes présents sur le serveur. Il faut seulement implémenter les interfaces jakarta.ws.rs.client. ClientRequestFilter et jakarta.ws.rs.client.ClientResponseFilter.

Les étapes WriteTo Chain et ReadFrom Chain sont identiques aux traitements décrits côté serveur. Il est même possible d’utiliser les mêmes classes côté serveur et côté client si besoin.

L’étape HTTP Invocation permet d’envoyer la requête vers le serveur dont les traitements sont matérialisés par l’étape Request/Response.

La dernière étape nommée Reponse Processing correspond à la lecture et au traitement de la réponse obtenue.

3. La mise en place du projet et des dépendances

Dans le cadre de l’ouvrage, l’application cliente sera développée sous la forme d’une console Java classique à partir d’un projet Gradle nommé Projet_Client_REST.

Lorsque le projet est créé, il reste à modifier le fichier build.gradle pour mettre en place les dépendances nécessaires :

 Pour l’implémentation Jersey, ajoutez les dépendances suivantes :

implementation group: 'org.glassfish.jersey.core', name: 
'jersey-client', version: '3.0.3' 
implementation...

Le côté client en JavaScript

1. Introduction

La notion de service web REST est souvent associée à la notion d’application cliente accessible au travers d’un navigateur web. Le langage utilisé pour communiquer directement avec le service web est alors le JavaScript, avec la technologie AJAX. Aujourd’hui, ce langage est très populaire et permet de réaliser des applications web riches et adaptées aux attentes des utilisateurs en termes de fonctionnalités et d’ergonomie. Le JavaScript peut s’utiliser seul mais il est souvent utilisé au travers de frameworks comme Angular, React ou encore Vue.js pour ne citer que les plus populaires. L’objectif de cette section n’étant bien sûr pas d’appréhender ce langage et ces frameworks. Elle se cantonnera à présenter le fonctionnement de base pour interroger un service web REST depuis un programme écrit en JavaScript. L’API Fetch sera utilisée pour réaliser les requêtes AJAX. Elle est nativement disponible dans les navigateurs modernes.

2. La mise en place du projet

Cette section va s’appuyer sur un nouveau projet nommé Projet_WEB_Client_REST. Pour le créer, il suffit d’utiliser le menu File - New - Dynamic Web Project et de remplir la boîte de dialogue ainsi :

images/06EP25N.png

Il pourra ainsi être exécuté par le serveur Tomcat. N’hésitez pas à supprimer les répertoires WEB-INF et META-INF ainsi générés. Ils ne sont pas utiles dans ce projet. Pour finir, ajoutez un fichier index.html dans le répertoire webapp :

images/06EP26N.png

Voici la structure de base de ce fichier :

<!DOCTYPE html> 
<html> 
   <head> 
       <meta charset="UTF-8"> 
       <title>Page de démonstration d'un client web REST</title> 
       <script type="text/javascript"> 
           //LE CODE JAVASCRIPT 
       </script> 
   </head> 
   <body> 
       <!-- L'affichage des informations en HTML --> 
   </body> 
</html> 

La partie...

Conclusion

Ce chapitre a permis de faire un premier tour d’horizon sur la mise en œuvre des services web REST. Pour approfondir le sujet, n’hésitez pas à consulter la documentation officielle sur le site de Jakarta EE (https://jakarta.ee/) et du W3C.