Exercices - WS Rest - Les locations⚓︎
Récupération de l'application
Vous trouverez ici un projet Web présentant une application de location (type AirBnb) :
Pour le faire fonctionner, il suffit de l'importer dans eclipse, d'avoir un Runtime Environment nommé WildFly 24.0 Runtime et une JDK1.8.
Il faut également avoir une base de données nommée location_db :
create database location_db;
Pour créer la datasource sur le serveur WildFly, vous pouvez l'ajouter depuis la console d'administration du serveur, ou plus simplement en ajoutant au bon endroit le code suivant, dans le fichier 📄standalone.xml
, lorsque le serveur est éteint (il faut bien sûr adapter le port, le login et le mot de passe) :
<datasource jndi-name="java:/LocationDB" pool-name="LocationDB">
<connection-url>jdbc:mysql://localhost:3306/location_db</connection-url>
<driver-class>com.mysql.cj.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password>root</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
<validate-on-match>true</validate-on-match>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
</validation>
</datasource>
IHM
L'application est maintenant disponible ici : http://localhost:8080/LocationApp/.
Cette IHM sert uniquement à vérifier que les 4 locations sont bien présentes dans la base de données. Nous ne ferons ici aucune modification sur cette IHM.
Dans ce TD, nous allons uniquement ajouter une partie Web Services pour exposer à d'autres applications certaines fonctionnalités.
L'objectif de ce TD est de compléter le projet fourni en proposant des WS Rest exposant les méthodes suivantes :
- Récupération d'une location (à partir de son identifiant).
-
Récupération des toutes les locations présentes dans la BDD.
- Il doit être possible de trier les locations (sur leur ville uniquement) de manière croissante ou décroissante.
- Il doit être possible de filtrer les locations par ville.
-
Suppression d'une location (à partir de son identifiant).
- Suppression de toutes les locations (ce service ne sera pas implémenté).
- Création d'une location.
- Mise à jour totale d'une location (à partir de son identifiant).
- Mise à jour partielle d'une location (à partir de son identifiant).
Exercice 1 - Spécification de l'API⚓︎
Exercice 1 - Spécification de l'API
Réfléchissez à la structure de vos URLs, et au verbe HTTP utilisé pour implémenter cette liste de service.
:warning: Il faut bien sûr respecter les différentes règles d'une architecture REST (les 3 premières en fait) (Cf. cours à partir de la page 26).
Exercice 2 - Affichage de location(s)⚓︎
Exercice 2 - Affichage de location(s)
Implémenter les 2 premiers services.
Dans les deux cas, le client doit pouvoir choisir de recevoir les données en XML ou en JSON. Si la réponse est demandée dans un autre format, une erreur 406
doit être retournée.
Pour tester les Web Services Rest, nous allons utiliser Postman (Cf. ici).
- On sélectionne le verbe HTTP souhaité.
- On indique l'URL permettant d'accéder au service souhaité.
- On sélectionne l'onglet Params,
-
et on indique les éventuels paramètres de requête (les
QueryParam
). -
On sélectionne l'onglet Headers, si on souhaite ajouter des informations dans l'en-tête de la requête HTTP,
- par exemple pour indiquer ce qu'on souhaite comme type de retour.
- On envoie la requête.
- On obtient le code HTTP retour,
- et en dessous, l'éventuel résultat de la requête.
Nous allons utilises pour JaxRS pour implémenter des Web Services Rest en JEE (JaxRS est une spécification, Jackson est l'implémentation de cette spécification).
Pour cela, il suffit de créer une classe, WebApp
par exemple, qui implémente javax.ws.rs.core.Application
, et qui contient l'annotation @ApplicationPath
.
Il faut ensuite créer une seconde classe, qui sera le Web Service. Elle doit contenir une annotation @Path
. Chaque méthode qui correspond à un service doit également contenir cette annotation, ainsi que celle correspondant au verbe HTTP qui permet de l'appeler.
Concernant les paramètres des méthodes, il faut utiliser les annotations @QueryParam
, @PathParam
, @HeaderParam
ou @FormParam
en fonction du cas (Cf. cours).
Il faut utiliser l'annotation @Produces
, et utiliser les MediaType
(en faisant attention au package) pour indiquer que XML et JSON peuvent être générés. On peut ici indiquer une liste de formats acceptés, entre {}
. Si un autre format est demandé, une erreur 406
est automatiquement renvoyée.
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@XMLRootElement
sur le bean correspondant.Pour que les annotations de JaxB (par exemple
@XmlTransient
) fonctionnenent également lors du mapping en JSON, il faut ajouter le JAR jackson-annotations.jar
dans le dossier 📂/WEB-INF/lib
du projet (il n'y a pas besoin de l'ajouter dans le classpath, il faut uniquement qu'il soit présent dans ce dossier, comme les drivers JDBC). Vous pouvez télécharger la version 2.13.2 de ce JAR ici :
Accept
dans l'en-tête de la requête, avec la valeur application/json
ou application/xml
.
Enfin, pour pouvoir appeler les EJBs depuis cette classe, il faut rajouter l'annotation @Stateless
sur la classe.
Exercice 3 - Suppression de location(s)⚓︎
Exercice 3 - Suppression de location(s)
Implémenter les services de suppression.
Dans cet exercice, on n'implémentera que le service de suppression d'une seule location (le numéro 3).
Pour ceux-ci, l'utilisateur doit être identifié. Nous n'allons pas géré ici toute la partie authentification, mais nous allons imaginer que l'authentification est gérée en amont, et que lorsque l'utilisateur est correctement identifié, un jeton d'authentification lui est renvoyé. Ce jeton est ensuite utilisé, et ajouté dans le header des requêtes HTTP qui le nécessitent. Ce jeton doit être associé au code Authorization
(la liste des en-tête est disponible ici).
sequenceDiagram
participant Client du WS
participant Serveur (le WS)
Client du WS ->> Serveur (le WS): Connexion
Serveur (le WS) -->> Client du WS: Token envoyé (ici 42)
Client du WS ->> Serveur (le WS): Requêtes Rest ...
Ici, on se contentera de vérifier que le jeton Authorization
présent dans l'en-tête de la requête HTTP est 42
. Si la clef Authorization
n'est pas présente dans l'en-tête, une erreur 401
doit être renvoyée. Si cette clef est bien présente, mais avec une valeur autre que 42
, une erreur 403
doit être renvoyée. La liste des codes HTTP est disponible ici.
Pour le service de suppression d'une location donné, si cette location n'existe pas, il faut renvoyer une erreur 404
.
Quel code faut-il renvoyer lorsque la location a bien été supprimée (et que tout s'est bien passé) ?
-
Gérer l'en-tête de la requête HTTP.
Il faut penser à utiliser l'annotation
@HeaderParam
sur les paramètres concernés. Il faut utiliser l'interfaceHttpHeaders
pour les noms de ces paramètres, par exemple :☕ Code Java@HeaderParam(HttpHeaders.AUTHORIZATION)
-
Indiquer un code retour HTTP.
Pour cela, il faut que la méthode qui implémente le service retourne un objet
Response
. Pour renvoyer un401
par exemple, il suffira d'écrire le code suivant :☕ Code Javareturn Response.status(Status.UNAUTHORIZED).build();
Pour indiquer un code
200
, on peut directement écrire :☕ Code Javareturn Response.ok().build();
Exercice 4 - Création d'une location⚓︎
Exercice 4 - Création d'une location
Implémenter le service de création (5).
Il faut pouvoir l'appeler via un formulaire HTML, ou en envoyant directement toutes les informations dans le corps de la requête, au format XML ou JSON. Si un autre format est utilisé, une erreur 415
doit être renvoyée.
Quel code doit être renvoyé lorsque tout se passe bien ?
Bonus : Dans la création depuis un formulaire, ajouter la possibilité d'indiquer l'URL d'une image.
Bonus : Pour ce service également, l'utilisateur doit être identifié.
-
Créer une location
Il y a deux manières de créer une location :
- En envoyant directement toutes les informations dans le corps de la requête, en JSON ou en XML par exemple.
- En envoyant des informations en paramètres de la requête, exactement comme lorsqu'on soumet un formulaire HTML. On utilise alors des
FormParam
.
Il va donc y avoir deux méthodes pour créer une location, donc les signatures sont :
☕ Code Java// Méthode appelée lorsqu'on ajoute toutes les informations dans le corps de la requête. public Response createLocation(LocationBean locationBean) { ... } // Méthode appelée lorsqu'on soumet un formulaire HTML. public Response createLocation(@FormParam("address") String address, @FormParam("city") String city, @FormParam("nightPrice") Double price, @FormParam("zipCode") Integer zipCode) { ... }
Pour appeler la première méthode, il faut :
- Sélectionner l'onglet Body,
- cocher raw,
- sélectionner JSON ou XML,
- et enfin indiquer les données, au format JSON ou XML selon :
JSON{ "nightPrice": 654.3, "address": "64 avenue Jean Portalis", "city": "Tours", "zipCode": "37200" }
XML<locationBean> <nightPrice>654.3</nightPrice> <address>64 avenue Jean Portalis</address> <city>Tours</city> <zipCode>37200</zipCode> </locationBean>
Pour appeler la deuxième méthode, il faut :
- Sélectionner l'onglet Body,
- cocher x-www-form-urlencoded,
- et enfin indiquer les données, comme paramètre de la requête (
@FormParam
) :
-
Obtenir une erreur
415
si le format n'est pas le bon.Cela est automatique, à partir du moment où tout les format acceptés sont recensés explicitement dans les annotations
@Consumes
des deux méthodes de création.
Exercice 5 - Mise à jour d'une location⚓︎
Exercice 5 - Mise à jour d'une location
Implémenter les services permettant une mise à jour totale (6) et une mise à jour partielle (7) d'une location.
On implémentera que la partie concernant la mise à jour via des données envoyées en JSON ou en XML. On n'implémentera pas la partie concernant la mise à jour d'une location via un formulaire HTML.
Bonus : Pour ces services également, l'utilisateur doit être identifié.