Développer une API REST avec Spring Boot

Article invité rédigé par


Le framework Spring Boot permet de créer rapidement des API Rest solides selon une architecture de code respectant le modèle MVC.

Dans ce document, la structure principale ainsi que ses éléments principaux seront présentés de façon à construire graduellement le squelette d’une API Rest.

Le code complet est disponible sur GitHub:

Structure Générale

Annotations

Au cœur de Spring Boot se trouvent les annotations qui servent à simplifier la tâche au développeur. Pour créer un contrôleur, il suffit de créer une classe et de l’annoter @RestController et de lui affected un point d’accès. Chacune des méthodes aura l’annotation @RequestMapping qui indique quel chemin de l’API la méthode couvre et quelle méthode HTTP lui correspond.

Ces annotations permettent à simplifier le code et à le rendre plus lisible. Le framework s’occupe de démarrer le serveur web et de rediriger les requêtes aux méthodes concernées.

Autowiring

Le principe d’autowiring est un autre concept important dans Spring Boot. Simplement en ajoutant l’annotation @Autowired au constructeur, Spring s’occupera d’instancier la classe directement en injectant les dépendances décrites dans les paramètres du constructeur, par exemple des services, repositories ou encore des objets de contexte. Pour chacun des objets injectés, il s’agira d’un c, c’est-à-dire que chacune des classes qui ont le même service comme dépendance recevront la même instance du service.

Controller

Dans notre exemple simple, nous utiliserons un contrôleur simple qui affiche les détails de livres et permet d’ajouter des nouveaux livres. Il nous faut donc un modèle permettant de représenter un livre, via une classe re. Cette classe ne contient que des getters et setters pour le numéro identifiant (ID), le titre du livre et le nom de l’auteur.

Pour le contrôleur, nous aurons donc une classe nommée BookController avec l’annotation @RestController pour indiquer à Spring de quel type de classe il s’agit.

La première méthode de l’api, getBook ressemblera donc à ceci:

Les annotations indiquent que la requête concerne la route /books/{bookId} et le paramètre bookId est une variable qui se retrouve dans la route. On peut donc démarrer l’application et se diriger sur http://localhost:8080/books/5 pour recevoir l’objet Book sous forme de json:

On voit également que l’attribut ID change selon ce que la route contient, ce qui démontre que l’attribut passé dans l’URL est bien reçu par la méthode du contrôleur.

Service et Repository

Dans l’exemple ci-dessous, l’objet Book est créé et retourné directement dans le contrôleur. En pratique, il faudrait séparer cette responsabilité pour respecter le design pattern utilisé avec Spring Boot. Le contrôleur devrait donc faire appel au Service, qui lui fait appel au Repository. La présence du Repository nous permet également d’avoir une persistance de la donnée, par exemple via une base de données SQL. Dans le cadre de notre exemple, nous utiliserons un Repository in memory.

Il faut d’abord créer les interfaces nécessaires:

Évidemment, le but de ces interfaces est encore une fois de séparer le comportement attendu de l’implémentation.

C’est ici que le principe d’autowiring décrit précédemment entre en jeu : dans le constructeur du contrôleur, nous allons injecter le service:

De cette façon, le contrôleur peut maintenant faire appel à BookServiceImpl et nous pouvons réusiner la méthode getBook() :

Le même principe s’applique pour l’implémentation du service, pour qu’il ait accès au repository.

Dans notre exemple, le service se contente d’appeler le Repository, donc ses méthodes peuvent sembler un peu superflues, par exemple:

Mais plus le code évolue et se complexifie, le service risque d’évoluer également et il pourrait faire appel à plusieurs repositories différents, par exemple un repository contenant de l’information supplémentaire sur les auteurs que le service devrait assembler avec l’information d’un livre.

Pour ce qui est du repository, nous pouvons créer son implémentation, BookRepositoryInMemory, en nous servant d’une simple list d’objets Book comme « base de données » :

Nous pouvons ensuite coder les autres méthodes du contrôleur:

 

Et c’est ici que nous voyons encore une fois comment Spring nous simplifie la vie: Le framework s’occupe de transformer la liste d’objets Book en une liste JSON, et dans le cas de addBook, Spring transforme l’objet JSON reçu dans le corps de la requête POST en objet Book.

Étapes restantes

Selon les pratiques que j’ai vues en pratique lors de mon stage, les autres étapes restantes dans cet exemple seraient d’utiliser un Data Transfer Object (DTO) au lieu d’utiliser directement des entités Book, car la classe Book pourrait éventuellement contenir de la logique d’affaire. Par la suite, un sérialiseur serait nécessaire pour convertir une entité en DTO et vice-versa.

Un assembleur de ressource serait également utilisé pour afficher le résultat en tant que ressource au lieu d’un entité directement, avec des métadonnées tel qu’un self-link, ou encore pour ajouter de la pagination dans le cas de l’affichage d’une liste.

Intercepteurs

Un autre aspect important de Spring Boot est la nation des intercepteurs. Ils permettent d’exécuter du code sur chaque requête de façon à initialiser des paramètres globaux, par exemple des en-tête HTTP comme la langue (Accept-Language).

En utilisant les intercepteurs, on peut éviter la répétition de code par exemple lors de la gestion d’authentification avec un jeton OAuth2. Au lieu de l’avoir dans chaque contrôleur, il suffit d’avoir un intercepteur qui s’occupe de traiter le jeton pour chaque requête et de placer l’information dans un objet qui pourra être injecté aux classes qui en ont besoin.

Dans notre exemple, nous allons simplement passer un paramètre « user » en tant qu’en-tête HTTP. Il faut d’abord une classe simple qui servira de component qui stocke l’information reçue:

 

L’annotation @Component nous permettra d’injecter un objet ApplicationContext dans les classes qui en ont besoin. On peut ensuite créer la classe HeadersInterceptor. En surchargeant la méthode preHandle de sa classe parent HandlerInterceptorAdapter, on peut accéder aux information de la requête HTTP et modifier l’objet ApplicationContexte qui a été injecté :

 

Il faut ensuite enregistrer l’intercepteur dans la classe InterceptorConfigu qui hérite de WebMvcConvigurer :

 

L’annotation @Configuration indique à Spring que cette classe contient de la configuration globale, et le code contenu dans la classe sera exécuté au démarrage de l’application.

Il serait également possible d’indiquer que l’intercepteur ne concerne que certaines routes et doit en exclure d’autre:

 

Afin de tester l’intercepteur de façon concrète, nous pouvons créer un contrôleur simple (UserController) qui retourne simplement l’information contenue dans l’objet ApplicationContext :

 

En faisant une méthode GET à http://localhost:8080/user avec l’entête « user », on peut voir l’information a correctement été interceptée :

Requête avec Postman vers l'API REST

Requête avec Postman

 

Documentation Swagger

Swagger est un outil de documentation qui permet de simplifier la tâche de documentation en s’intégrant directement au code. Il est facile de l’intégrer à Spring en ajoutant une dépendance dans le fichier de configuration Maven (pom.xml). La procédure d’installation est décrite plus en détail dans le tutoriel suivant:

Une fois installé et configuré, une page swagger-ui.html devient disponible et liste les contrôleurs et leurs méthodes associées :

 

Il est possible de personnaliser les information en utilisant des annotations sur les classes des contrôleurs ainsi que leurs méthodes, par exemple:

 

Les informations définies dans ces annotations se retrouvent dans la page swagger-ui.html :

Il y a beaucoup d’autres personnalisations possibles avec d’autres annotations, par exemple pour indiquer quels en-têtes HTTP sont nécessaires aux méthodes, ou les codes HTTP qui peuvent être retournés.

Comments

comments

0 réponses

Répondre

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *