Aller au contenu

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 :

  1. Copier le résultat (le fichier JSON) de la requête dans la partie gauche.
  2. 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.locationapp.model.address
    
  3. Dans le champ Class name, indiquer le nom de la classe principale. Ici, on peut par exemple indiquer
    WsAddressResult
    
  4. Dans Source type, sélectionner JSON.
  5. 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.

jsonschema2pojo

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.

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.locationapp.dao.AddressDao :

☕ Code Java
public List<Feature> getAddresses(String search);

Et son implémentation, la classe fr.univtours.polytech.locationapp.dao.AddressDaoImpl. Pour cela, nous allons utiliser les classes fournies par JaxRS :

☕ Code Java
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 :

☕ Code Java
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 :

  1. Commençons par créer le service métier appelant ce DAO. Pour cela, créer l'EJB fr.univtours.polytech.locationapp.business.AddressBusinessLocal (nous n'allons pas créer de Remote ici), contenant une méthode :

    ☕ Code Java
    public List<Feature> searchAddresses(String search);
    

    :warning: Ne pas oublier d'utiliser l'annotation @Local.

  2. Créer l'implémentation de cet EJB, qui appelle la couche DAO.

    :warning: Ne pas oublier d'utiliser l'annotation @Stateless.

  3. Créer une servlet, qui sera appelée via l'URI /searchAddress, contenant l'implémentation de doGet et de doPost :

    • doGet redirige l'utilisateur vers une JSP addresses.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 vers addresses.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 uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <!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/LocationApp/searchAddress (il faut penser à mettre à jour le nom de l'application Web et du port).