Exemple d'un client HTTP déclaratif de Micronaut en langage Kotlin
Destiné à la création de microservices sur la machine virtuelle Java, le framework Micronaut dispose de fonctionnalités impressionnantes facilitant l'activité du développeur.
L'une d'elles est notamment la possibilité de construire des clients HTTP réactifs, de manière déclarative, afin d'appeler un autre microservice Micronaut par exemple, ou bien de consommer une API externe.
C'est ce nous allons voir, au travers d'un exemple d'un tel client, dans une application Micronaut développée en langage Kotlin, pour consommer l'API SWAPI (The Star Wars API), et dont le code source est disponible ici : declarative-http-client.
Définition de l'interface du client HTTP
Lorsque l'on parle d'un client HTTP déclaratif, c'est qu'il suffit d'utiliser l'annotation @Client
du package io.micronaut.http.client
sur une interface ou une classe abstraite pour définir un client dont l'implémentation sera automatiquement prise en charge par Micronaut au moment de la compilation ; d'autres annotations correspondants aux différents verbes HTTP viennent aussi décorer les méthodes du type pour indiquer les types de requête à effectuer lors de l'invocation de ces méthodes.
Pour commencer simplement, voyons comment interroger l'API SWAPI pour obtenir des informations sur un personnage de la célèbre saga ; cela s'effectue par l'envoi d'une requête HTTP de type GET sur l'URI https://swapi.co/api/people/{id}
, où {id}
représente l'identifiant de la ressourse dont on veut obtenir une représentation.
Dans le code ci-après, l'annotation @Client
placée sur l'interface SwApiClient
permet de définir l'URL de base de l'API SWAPI à invoquer, tandis que sur la méthode getCharacterUsingMap
, on trouve une annotation @Get
permettant de spécifier une URL relative : cela permettra, lors de l'appel à la méthode, d'effectuer au final une requête de type GET vers la ressource cible.
Il y a ici deux choses à noter :
- nous utilisons ici une URL relative paramétrée avec
{id}
, afin de transmettre l'id
de la ressource à partir du paramètre de même nom passé dans la méthode ; - le type du retour de la méthode, devant être un type supporté par Micronaut, spécifie sous quel type la ressource demandée devra être retournée ; en l'occurence, la représentation JSON du personnage sera convertie en un objet de type
Map<String,*>
, encapsulé dans le type réactifSingle
.
1 2 3 4 5 6 |
|
Utilisation du client HTTP
Pour utiliser le client HTTP ainsi défini par une interface, un moyen possible d'en obtenir une instance (plus précisément une instance du type implémentant l'interface), est celui de l'injection que Micronaut résout à la compilation.
Dans le code source du projet d'exemple Micronaut (declarative-http-client), une instance du client est obtenue par le contexte d'application (méthode main
de l'objet Application
), puis l'on invoque la méthode getCharacterUsingMap
en lui passant la valeur 1
qui est l'id
pour Luke Skywalker :
1 2 3 |
|
On se contente dans le code ci-dessus d'afficher la valeur présente dans l'objet Single
, par l'expression lambda passée à la méthode subscribe
; lors de l'exécution, la console affiche les informations de la ressource Luke Skywalker, sous la forme d'une Map (transformée en chaîne de caractères) :
{name=Luke Skywalker, height=172, mass=77, hair_color=blond, skin_color=fair, eye_color=blue, birth_year=19BBY, gender=male, homeworld=https://swapi.co/api/planets/1/, films=[https://swapi.co/api/films/2/, https://swapi.co/api/films/6/, https://swapi.co/api/films/3/, https://swapi.co/api/films/1/, https://swapi.co/api/films/7/], species=[https://swapi.co/api/species/1/], vehicles=[https://swapi.co/api/vehicles/14/, https://swapi.co/api/vehicles/30/], starships=[https://swapi.co/api/starships/12/, https://swapi.co/api/starships/22/], created=2014-12-09T13:50:51.644000Z, edited=2014-12-20T21:17:56.891000Z, url=https://swapi.co/api/people/1/}
Cette Map correspond à l'objet JSON suivant retourné par l'API SWAPI :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Plus loin avec de la liaison de données
Pour des besoins de traitement, il serait plus clair de manipuler des types prédéfinis plutôt que des Maps ; c'est aussi prévu par Micronaut !
Définissons une méthode d'interface presque identique à la précédente, nommée getCharacter
, mais retournant le type Single<Character>
, où Character
est une classe de données personnalisée représentant un personnage :
1 2 3 4 5 6 7 8 9 |
|
Nous définissions la classe Character
avec les propriétés qui nous intéressent, ici name
, gender
et birth_year
:
1 2 |
|
Character
est une classe data pour laquelle on a défini une valeur par défaut pour chacun des paramètres du constructeur, de sorte que le mécanisme de liaison de données puisse fonctionner correctement (c'est comme si on avait défini un constructeur par défaut pour Character
).
Ainsi dans le lambda passé à la méthode subscribe
, on dispose maintenant d'un objet de type Character
correctement initialisé avec les valeurs JSON correspondant aux propriétés de la classe :
1 2 3 |
|
Intéressons-nous maintenant à l'ensemble des personnages de la saga, qui peut s'obtenir grâce à l'URL https://swapi.co/api/people/
de l'API SWAPI ; un appel vers cette URL avec une commande HTTP GET permet d'obtenir la première page contenant les représentations des 10 premiers personnages sur un total de 87, dans la propriété JSON results
.
Par ailleurs, la structure JSON retournée comporte également les propriétés next
et previous
permettant à l'appelant de naviguer vers les différentes pages de l'ensemble de données.
Ajoutons une troisième méthode, getPeople
, à notre interface SwApiClient
, afin de permettre l'interrogation de l'ensemble des personnages, pour une page donnée :
1 2 3 4 5 6 7 8 9 10 11 12 |
|
La classe de données PeopleResponse
est définie comme suit :
1 2 |
|
Notez que cette classe de données comporte en particulier les propriétés next
et results
:
next
, qui permettra de récupérer l'URL de la page suivante ;results
, qui est de typeList<Character>
, et contiendra la liste des personnages de la page visitée.
Ainsi, la liaison de données s'appliquera aussi bien pour la structure d'une page, que pour les résultats qu'elle retournera, sous la forme d'une liste de Character
.
A titre d'exemple, le code source du projet declarative-http-client, contient du code qui parcourt l'ensemble des pages pour obtenir la totalité des personnages sous la forme d'une liste.
Documentation et guide
Pour en savoir plus sur les clients HTTP déclaratifs de Micronaut, reportez-vous à la section Declarative HTTP Clients with @Client du guide utilisateur du framework.
Je vous recommande également de lire le guide Micronaut HTTP Client accessible depuis la page Guides, et disponible dans différents langages.