Préambule
La démonstration 2 du TD 1 comporte des faiblesses au niveau du code, ainsi que des situations non traitées, avec notamment :
- le cas où un objet valant null est renvoyé par un service, ce qui conduit à une réponse http vide,
- les requêtes http avec un "body", par exemple dans le cas de requête POST, PUT, ...
L'objectif de ce cours est de présenter comment traiter ces deux points particuliers, tout en décrivant quelques fonctionnalités bien pratiques, à savoir la pagination des résultats de requête, le paramétrage "manuel" d'une réponse http, ou encore la lecture d'un fichier csv (soit intégré à l'application, soit uploadé) pour remplir la BdD
Le projet de démonstration des points abordés est téléchargeable ci-dessous :
springboot-td2-src.tgz
1°/ Gestion d'erreurs
Par défaut, si une route demandée n'est pas valide, ou qu'il y a une erreur interne qui génère une exception, spring renvoie au client une page html basique. Dans le cas d'une API REST, c'est plutôt génant puisqu'il faudrait plutôt renvoyer un objet JSON représentant l'erreur, avec au minimum un descriptif de l'erreur.
Il est relativement simple de changer ce comportement par défaut, en ajoutant quelques classes pour représenter les cas d'erreur ainsi que ces classes qui vont "intercepter" les exceptions non traitées pour en faire des réponses http comme on le désire. Plus en détail, il s'agit au minimum de :
- créer une classe d'exception (par exemple, héritant de RuntimeException) pour représenter des cas d'erreur lors des requêtes. Par exemple, cela peut être une classe ResourceNotFoundException, représentant le fait que l'on n'a rien trouvé en BdD par rapport aux paramètres de la requête.
- créer une classe permettant de représenter les objets JSON de type erreur que l'API va renvoyer au client. Par exemple, on peut créer une classe ErrorDTO, contenant un numéro et un message d'erreur.
- créer une classe annotée avec @RestControllerAdvice, qui va définir des méthodes de type "gestionnaires d'exception", celles-ci retournant un objet décrivant l'erreur. Par exemple, cette classe peut être nommée ExceptionHandler, qui définit un gestionnaire d'exception resourceNotFoundHandler(), qui retourne un ErrorDTO.
Au delà de ce minimum, il est conseillé de mettre en place une stratégie de gestion des erreurs plus complète, avec par exemple :
- Une (ou plusieurs) classe(s) listant les erreurs possibles grâce à une HashTable<Integer, String> dont la clé est le numéro d'une erreur et la valeur le message d'erreur.
- De faire plusieurs classes d'exception pour chacun des grands types d'exception, voire des sous-classes à celles-ci.
- D'ajouter un attribut indiquant le contexte qui a conduit à une erreur dans la classe servant à représenter les objets JSON d'erreur transmis aux client. Par exemple, si une ressource n'est pas trouvée, le contexte pourrait être une chaîne de caractères indiquant la requête qui a conduit à l'échec.
- D'ajouter des attributs aux classes d'exception pour que les gestionnaires puissent facilement retourner une instance d'objet représentant l'erreur. Avec les hypothèses ci-dessus, cela implique d'ajouter des attributs pour le numéro et le contexte, le message étant déjà par défaut dans toute classe d'exception.
Dans la démonstration, ces différents points sont utilisés, avec en premier lieu la classe ErrorList dont voici un extrait :