Exercices - Création d'un client Rest⚓︎
Toujours dans notre application de locations, on peut imaginer rajouter une page Web permettant l'ajout d'un nouveau logement. Pour cela, il faudrait pouvoir saisir son adresse. Lorsque vous remplissez un tel formulaire sur une page Web, il y a souvent des propositions d'adresses qui apparaîssent lorsqu'on commence la saisie. Nous allons mettre en place cela (en partie).
Nous allons donc utiliser un Web Service (externe à notre application) comme source de données. Nous allons donc rajouter une couche DAO, qui ne se connecte pas à une base de données cette fois, mais à ce Web Service.
Exercice 1 - Prise en main de l'API⚓︎
Prise en main de l'API
Pour cela, nous allons utiliser l'API nationale permettant de chercher une adresse en France.
Vous pouvez effectuer quelques tests, avec Postman par exemple, pour la prendre en main.
Elle est disponible ici : https://adresse.data.gouv.fr/api-doc/adresse
Nous allons utiliser l'URI /search
avec le verbe HTTP GET
et deux paramètres de requête (c'est-à-dire des "query parameters") :
q
: Permet d'indiquer l'adresse recherchée.limit
: Le nombre (maximum) de résultats renvoyés.
Il y a d'autres paramètres possibles, mais ils ne nous sont pas nécessaires ici.
Par exemple, vous pouvez effectuer un test avec https://api-adresse.data.gouv.fr/search/?q=64%20avenue%20jean%20portalis&limit=15, qui renvoie, à l'heure où ceci est écrit, 4 résultats.
Remarque : Comme nous n'utilisons que le verbe HTTP GET
, que nous ne modifions ni l'en-tête, ni le corps de la requête, et que le résultat est par défaut envoyé au format JSON, il est possible d'effectuer la requête directement dans un navigateur, sans passer par Postman. Je vous conseille cependant tout de même de l'utiliser, afin de bien prendre l'habitude de l'utiliser.
Exercice 2 - Génération du modèle⚓︎
Génération du modèle
Il nous faut transformer ce résultat JSON en Java Beans. Nous pourrions créer la couche modèle correspondante à la main, puisque le format du résultat est indiqué ici, mais ce serait fastidieux. Il est possible de générer automatiquement ce code.
On peut pour cela par exemple utiliser le site https://www.jsonschema2pojo.org/. Pour cela :
- Copier le résultat (le fichier JSON) de la requête dans la partie gauche.
- Dans le champ Package, indiquer le package dans lequels les Java Beans (la couche modèle) va être générée. Ici, on peut par exemple indiquer
fr.univtours.polytech.location.model.address
- Dans le champ Class name, indiquer le nom de la classe principale. Ici, on peut par exemple indiquer
WsAddressResult
- Dans Source type, sélectionner JSON.
- Cliquer sur
Zip , télécharger le résultat et le placer dans le dossier 📂src/main/java
(le dossier contenant les sources Java) du projet Web.
Ca y est, notre modèle est généré !
Depuis notre application Java, lorsque nous exécuterons le Web Service, nous récupérerons un objet WsAddressResult
, contenant lui même une liste de Feature
. Cet objet Feature
correspond à une adresse.
Il contient lui-même un objet Properties
qui permettra d'accéder aux informations sur chacune des adresses renvoyées.
Erreurs dans le code généré
Il y a des erreurs dans le code généré. En effet, l'annotation Generated
est utilisée ici. Mais l'import correspondant est
import javax.annotation.Generated;
import jakarta.annotation.Generated;
Exercice 3 - Création du client Rest⚓︎
Création du client Rest
Il nous reste maintenant à appeler ce Web Service, depuis notre application Java et non plus depuis un navigateur ou depuis Postman uniquement. Plus précisément, il nous reste à appeler ce Web Service depuis la couche DAO de notre application Java.
Commençons donc par créer la couche DAO. Elle ne contient ici qu'une seule méthode, qui renvoie une liste d'adresse (c'est-à-dire une liste de Feature
), et qui prend en paramètre une chaîne de caractère : l'adresse recherchée.
Voici donc l'interface fr.univtours.polytech.location.dao.AddressDao
:
public List<Feature> getAddresses(String search);
Et son implémentation, la classe fr.univtours.polytech.location.dao.AddressDaoImpl
. Pour cela, nous allons utiliser les classes fournies par JaxRS. Pour rappel, l'URL que nous devons appeler depuis le DAO, que nous allons donc devoir générer ici, ressemble à cela :
https://api-adresse.data.gouv.fr/search/?q=64%20avenue%20jean%20portalis&limit=15
private static String URL = "https://api-adresse.data.gouv.fr";
@Override
public List<Feature> getAddresses(String search) {
// Instanciation du client.
Client client = ClientBuilder.newClient();
// On indique l'URL du Web Service.
WebTarget target = client.target(URL);
// On indique le "end point" (on aurait aussi pu directement le mettre dans
// l'URL).
// C'est également avec cette méthode qu'on pourrait ajouter des "path
// parameters" si besoin.
target = target.path("search");
// On précise (lorsqu'il y en a) les "query parameters".
target = target.queryParam("q", search);
target = target.queryParam("limit", 15);
// On appelle le WS en précisant le type de l'objet renvoyé, ici un
// WsAddressResult.
System.out.println(target.getUri());
WsAddressResult wsResult = target.request(MediaType.APPLICATION_JSON).get(WsAddressResult.class);
return wsResult.getFeatures();
}
Remarque sur le code
On pourrait écrire tout cela de manière condensée :
private static String URI = "https://api-adresse.data.gouv.fr/search";
@Override
public List<Feature> getAddresses(String search) {
Client client = ClientBuilder.newClient();
Result result = client.target(URI).queryParam("q", search).queryParam("limit", 15)
.request(MediaType.APPLICATION_JSON).get(WsAddressResult.class);
return result.getFeatures();
}
Exercice 4 - Les couches métier et présentation⚓︎
Les couches métier et présentation
Nous allons ajouter une petite couche présentation, pour utiliser ce que nous avons fait :
-
Commençons par créer le service métier appelant ce DAO. Pour cela, créer l'EJB (c'est-à-dire la couche métier)
fr.univtours.polytech.location.business.AddressBusiness
, contenant une méthode :☕ Code Javapublic List<Feature> searchAddresses(String search);
-
Créer l'implémentation de cet EJB, qui appelle la couche DAO.
⚠️ Ne pas oublier d'utiliser l'annotation
@Stateless
. -
Créer une servlet, qui sera appelée via l'URI
/searchAddress
, contenant l'implémentation dedoGet
et dedoPost
:doGet
redirige l'utilisateur vers une JSPaddresses.jsp
contenant un champ pour saisir une adresse.doPost
récupère cette adresse saisie, appelle l'EJB effectuant la recherche, et redirige l'utilisateur versaddresses.jsp
qui affiche cette liste.
☕ Code Java - La servlet@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // On place une liste vide dans la requête. request.setAttribute("ADDRESSES", new ArrayList<Feature>()); // On redirige vers la JSP. request.getRequestDispatcher("addresses.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // On récupère ce que l'utilisateur à saisi. String search = request.getParameter("search"); // On exécute la requête correspondante. List<Feature> addresses = this.addressBusiness.searchAddresses(search); // On place le résultat de la recherche dans la requête. request.setAttribute("ADDRESSES", addresses); // On redirige vers la JSP. request.getRequestDispatcher("addresses.jsp").forward(request, response); }
☕ La JSP<%@ taglib prefix="c" uri="jakarta.tags.core" %> <!DOCTYPE html> <html> <head> <title>Rechercher une adresse</title> </head> <body> <form action="searchAddress" method="post"> <input type="text" name="search"/> <input type="submit" value="Rechercher"/> </form> <table> <tr> <th>Adresse</th> <th>Ville</th> <th>Code postal</th> </tr> <c:forEach var="address" items="${requestScope.ADDRESSES}"> <tr> <td> ${address.properties.name} </td> <td> ${address.properties.city} </td> <td> ${address.properties.postcode} </td> </tr> </c:forEach> </table> </body> </html>
La page de recherche est maintenant accessible depuis l'URL http://localhost:8080/location/searchAddress.
Exercice 5 - Intégration de cette API dans notre application⚓︎
Intégration de cette API dans notre application
Intégrer cette API dans l'application, c'est-à-dire que sur la page d'accueil (http://localhost:8080/location/), pour chaque location, les coordonnées GPS (latitude et longitude) doivent apparaître.
La recherche de l'adresse se fera en concaténant les champs address
et zipCode
du bean, en insérant un espace entre les deux. On vérifiera que l'API renvoie au moins un résultat, et si c'est le cas, on récupérera les informations du premier résultat.
Attention, ces données ne doivent pas être stockées en base, mais recalculées à chaque affichage de la liste.
Exercice 6 - Intégration de la météo⚓︎
Intégration de la météo
L'idée est d'afficher la température actuelle (au moment de la recherche) au lieu où se trouve la location.
Il est demandé d'afficher cette température dans les résultats affiché par l'API Web développée dans le TD précédent, puis vous afficherez également cette information dans la page de recherche (http://localhost:8080/location/).
À partir des résultats fournis par l'API Web adresse (latitude et longitude), vous appellerez l'API Openweathermap : https://openweathermap.org/current
Elle permet de récupérer la température en kelvin (⚠️) étant données la longitude et la latitude.
L'API Web Openweathermap
Pour utiliser l'API Openweathermap, il faut une clef.
Pour cela :
- Vous devez vous créer un compte ici : https://home.openweathermap.org/users/sign_up.
- Vous pouvez ensuite récupérer votre clef ici : https://home.openweathermap.org/api_keys.
Cette clef sera ajoutée en paramètre de requête (query parameter) à chaque requête.
Pour utiliser cette API Web, vous devrez ajouter trois paramètres de requêtes (dans n'importe quel ordre) :
appid
: La clef qui a été générée pour vous.lat
: La latitude de l'endroit où on souhaite connaître la température actuelle.lon
: La longitude de l'endroit où on souhaite connaître la température actuelle.
Conseils
-
Une façon de faire peut être d'ajouter un attribut
temperature
au beanLocationBean
.⚠️ Attention, ce champ ne doit pas être stocké en BDD, en effet, sa valeur change à chaque instant.
-
Ce champ pourra être mis à jour à chaque récupération de locations en base (c'est-à-dire lors de la récupération de toutes les locations, ou lors de la récupération d'une location via son id).
-
Il faudra certainement créer des DAO pour appeler les deux API Web utilisées, mais les 3 DAO qui existeront alors pourront être appelés depuis un seul EJB :
LocationBusinessImpl
.En effet, il n'y a pas de raison de créer d'autres classes dans la couche métier. Fonctionnellement, tout ce que nous faisons concerne les locations (et donc
LocationBusinessImpl
). -
Attention, si aucune adresse n'était renvoyée par l'API Web adresse, il ne faudrait pas qu'une exception Java (
NullPointerException
par exemple) ne soit levée.Dans ce cas là, la température devrait être à
Null
.
Il serait préférable que la clef de l'API Web Openweathermap utilisée soit stockée dans un fichier de configuration (et pas dans une classe Java).
Il serait également préférable sur les températures affichées soient arrondies au degré.