Prise en main de Vert.x
Préparation
Commencez par télécharger et décompresser l’archive contenant le code dont nous aurons besoin pour la session d’aujourd’hui.
Ouvrez le dossier dans VSCode. Vous devriez avoir la structure suivante :
- les sources de notre serveur sont dans le dossier
src; - un dossier
staticcontenant la page html du frontend; - un fichier
readme.mdavec des instructions sommaires pour lancer le projet.
Le serveur se lance à partir de la classe VertxServer.java dans le package fr.imta.addressbook.server. Vous pouvez lancer le serveur de la façon habituelle. C’est à dire en cliquant sur la flèche en haut à droite de VS Code lorsque vous éditez un fichier java ou avec le raccourcis clavier Ctrl+F5.
Si tout fonctionne bien le terminal devrait afficher :

Vous devriez alors pouvoir aller ici et voir cette page.

Depuis cette interface web vous pouvez visualiser et éditer des personnes et leur carnet d’adresses. En l’état seulement la partie “Personnes” de l’application fonctionne. Vous voyez un message d’erreur lorsque vous essayez d’accéder à la partie “Carnets”.
L’objectif de ce TP est comprendre le fonctionnement d’une application web moderne et d’être capable d’ajouter les fonctions qui permettront de faire fonctionner la partie “Carnets” de l’application.
Architecture d’une application web
L’application que vous voyez est composée de deux parties.
La partie visible - le frontend ou client
C’est la partie que vous voyez dans votre navigateur lorsque vous allez sur http://localhost:8080/. Cette partie est constituée de code HTML (contenu), CSS (style) et JavaScript (dynamisme) qui sont interprétés par votre navigateur et affichés à l’écran.
Notez que la partie visible peut prendre différentes formes comme une application mobile. Par exemple, vous pouvez utiliser Discord depuis votre navigateur web, votre smartphone ou votre PC. Ces différentes applications sont adaptées au support sur lequel elles fonctionnent.
La partie invisible - le backend ou serveur
Ces applications vont ensuite communiquer avec un ou plusieurs serveurs pour récupérer les données à afficher ou mettre à jour des données.
Par exemple, lorsque vous vous connectez à Discord, le client que vous utilisez va contacter les serveurs de Discord avec vos identifiants pour vous connecter. Ensuite le client pourra demander aux serveurs de lui envoyer la liste des nouveaux messages.
Dans notre exemple, lorsque vous ouvrez la page web http://localhost:8080/ dans votre navigateur,
celui-ci demande au serveur de lui envoyer le contenu de la page /. Cette page est stockée dans le fichier /static/index.html du projet.
Ensuite, la page est interprétée par votre navigateur.
Dans un premier temps, votre navigateur demande au serveur la liste des personnes ainsi que leurs détails pour pouvoir les afficher.
Ensuite, lorsque vous cliquez sur des boutons de la page web, votre navigateur envoye différentes requêtes pour effectuer les actions que vous demandez (p.ex. supprimer ou ajouter une personne).
Lorsque le serveur reçoit la requête, il la traite et renvoye une réponse qui est interprétée par votre navigateur et ainsi de suite.
L’interface entre le frontend et le backend
Pour pouvoir communiquer, il est nécessaire que le client et le serveur se mettent d’accord sur la façon de communiquer. On parle d’Interface de programmation (ou plus communément API -Application Programming Interface-). C’est l’API qui décrit l’ensemble des méthodes qu’un code peut appeler sur un serveur avec les arguments et réponses attendus ainsi que les erreurs éventuelles.
Vous noterez que l’on retrouve ce concept d’API partout dans la programmation. Par exemple, en Python, lorsque vous avez utilisé les classes de PyRat vous avez utilisé son API. Ou encore, en Java vous pouvez lire la page de documentation de la classe String qui décrit son API.
Dans notre cas, nous décrirons les API avec les informations suivantes :
- un verbe HTTP, décrivant un type d’action (get pour une consultation, delete pour une suppression, post pour une création, put pour une mise à jour)
- un chemin vers une ressource, exemple
/person. Ce chemin peut parfois contenir des informations, par exemple/person/1pour faire référence à la personne ayant l’identifiant 1. - des données potentielles, ou payload. Lorsque nous voudrons créer ou mettre à jour il faudra souvent que le client envoie des données au serveur pour faire le traitement. Exemple le JSON
{"firstname": "Balthazar", "lastname": "Picsou"}pour créer une personne. - les retours potentiels, avec un code indiquant si le traitement est réussi (200), si la ressource n’existe pas (404) ou si il y a eu une erreur lors du traitement (500) et un message.
Vous pouvez ouvrir cette page pour voir la liste des méthodes que notre serveur expose au client. Dans le cas du développement web, nous appelons ces méthodes routes.
Vous pouvez voir que le serveur de notre application de carnet d’adresse possède 10 routes, réparties en 2 catégories. 5 traitant les requêtes pour les personnes et 5 pour les carnets d’adresses.
De plus, pour chaque route vous avez :
- un code couleur pour chacune des actions HTTP présentées plus tôt (GET, POST, PUT et DELETE),
- le chemin de chacune de ces routes avec parfois des paramètres mis entre accolades (p.ex.
/person/{id}), - une description de la route.
Lorsque vous cliquez sur une route, l’interface affiche tous les détails sur la route en question avec, en particulier, une description des paramètres ainsi que des retours possibles de la route.
Par exemple, la route GET /person devrait toujours retourner une liste contenant les identifiants des personnes existantes.
Si vous avez lancé le serveur vous pouvez tester la route en cliquant sur “Try it out” en haut à droite d’une route puis sur “execute”. Vous devriez voir la réponse du serveur à la requête de votre navigateur.
Par exemple, ici vous pouvez voir le résultat de l’appel à la route GET /person.

Vous voyez la commande qui peut être utilisée pour effectuer l’appel depuis un terminal, l’URL ainsi que la réponse.
Exercice 1
Explorez les routes du groupe Person depuis l’interface web http://localhost:8080 ainsi que depuis cette page. Essayez de créer une personne depuis la page d’api et constater qu’elle est présente dans l’interface web lorsque vous rechargez la page. Faites de même pour la modification et la suppression de personnes.
Le fonctionnement du backend
Dans ce projet, nous n’allons aborder que le développement du backend. Pour ce tutoriel et la suite du projet vous allons utiliser une architecture similaire.
Le code de ce projet est structuré comme suit. Nous avons deux packages Java :
- le package
fr.imta.addressbook.modelqui contient la partie responsable du stockage des données, - le package
fr.imta.addressbook.serverqui contient la partie responsable du traitement des requêtes venant du client.
Voici le diagramme de classes de cette application :

Pour ce tutoriel la partie données contient 3 classes, une classe Database.java faisant office de base de données en mémoire et deux classes AddressBook.java et Person.java.
Ici, la base de données est un objet contenant deux listes d’AddressBook et Person et des méthodes pour manipuler ces listes (chercher un élément, créer un nouvel élément ou supprimer un élément).
Ces méthodes seront utilisées pour accéder aux données.
Le second package contient quand à lui le fichier VertxServer.java
qui est le point d’entrée du programme. Il initialise la base de
données, crée le serveur et le configure. Nous avons aussi un sous
package handlers contenant deux fichiers PersonHandler.java et
AddressBook.java. Ces fichiers contiennent les méthodes qui sont
exécutées lorsqu’une requête est faite au serveur.
La configuration du serveur
Ouvrez VertxServer.java. C’est dans la méthode start() que nous placerons tout le code pour indiquer quelles méthodes appeler lorsqu’une requête arrive sur notre serveur.
Dans l’ordre, commençons par créer un routeur et le configurer pour traiter les payload des requêtes et autoriser les requêtes de l’extérieur. Ce routeur est responsable de l’appel des bonnes méthodes lorsqu’une requête arrive au serveur.
Puis, nous instancions notre première classe qui traite les personnes (PersonHandler).
On parlera de handler pour désigner les méthodes qui seront appelées pour traiter les demandes des clients.
Nous lui passons la base de données en paramètre car elle aura besoin d’accéder à la base de données pour traiter les demandes.
Ensuite nous associons des méthodes de notre instance de PersonHandler à des routes.
Par exemple, lorsqu’un client effectue une requête GET à l’URL /api/person, le serveur appelle la méthode getId de notre objet ph.
Vous pouvez voir qu’en fonction du type de requête nous utilisons différentes méthodes du router (get(), post(), …).
Notez la syntaxe myVariable::methodName qui indique que l’on ne souhaite pas appeler la méthode mais donner à la fonction une référence sur la méthode qu’elle pourra utiliser plus tard.
Vous pouvez constater que les deux routes (GET et PUT) /person/:id sont génériques, elles capturent toutes les requêtes commençant par /person/ et suivi d’un mot. Lorsque vous déclarez une route vous pouvez indiquer des paramètres, que vous pourrez récupérer dans votre handler, avec la notation :parameterName.
Par exemple, la route /person/:id a un paramètre nommé id qui capture tous les mots qui suivent le /person/.
Vous verrez ensuite comment récupérer ces paramètres dans vos handlers.
Les handlers
PersonHandler.java
Regardez maintenant PersonHandler.java. Cette classe contient les méthodes que nous avons associées aux différentes routes précédentes.
Notez que ces méthodes sont public, ne retournent rien (void) et prennent en paramètre un RoutingContext.
Ces méthodes sont appelées par le serveur lorsque qu’une requête avec une route spécifique arrive.
L’objet RoutingContext que nous recevons en paramètre contient toutes les informations sur la requête et possède des méthodes pour envoyer la réponse désirée.
Astuce
Vous trouverez toutes les méthodes que vous pouvez utiliser sur un RoutingContext ici. Les méthodes que vous utiliserez le plus sont String pathParam(String), String queryParam(String), RequestBody body(), HttpServerRequest request(), HttpServerResponse response() ainsi que les méthodes end(String) et json(Object).
Exercice 1
Créer un nouveau handler pour la route GET /person de façon à ce qu’elle retourne la chaîne Hello world.
Que constatez vous lorsque vous essayez d’utiliser l’interface web ?
Correction
Exercice 2
Créer un second handler pour la route GET /person/:id pour retourner la chaîne Hello suivi du prénom de la personne correspondant à l’ID.
Par exemple pour /person/1 il faut retourner Hello Riri.
Si l’ID n’est associé à aucune personne alors répondre avec une erreur 404 et le message “Personne introuvable”.
Correction
Exercice 3
En vous inspirant de ce qui est fait dans PersonHandler.java et des méthodes dans Database.java, créez les méthodes de AddressBookHandler.java nécessaires pour les 5 routes manquantes.
Vous pouvez vous aider de la page d’API et de l’interface web pour tester le bon fonctionnement de vos routes.