Appels REST Apex
Objectifs de formation
Après avoir terminé ce module, vous pourrez :
- Émettre un appel pour recevoir des données d’un service externe.
- Émettre un appel pour envoyer des données à un service externe.
- Tester les appels à l’aide d’appels fictifs.
Vidéo de démonstration Trail Together
Vous souhaitez être guidé pas à pas par un expert pendant que vous travaillez sur cette étape ? Regardez cette vidéo qui fait partie de la série Trail Together sur Trailhead Live.
(Ce clip commence à 7 min 54 s, au cas où vous voudriez revenir en arrière et regarder à nouveau le début de l’étape.)
Bases des appels et du HTTP
Les appels REST sont basés sur le protocole HTTP. Vous comprendrez plus facilement comment les appels fonctionnent si vous vous familiarisez avec quelques points concernant le HTTP. Chaque requête d’appel est associée à une méthode HTTP et à un point de terminaison. La méthode HTTP indique le type d’action souhaité.
La requête la plus simple est la requête GET (GET est une méthode HTTP). La requête GET indique que l’expéditeur souhaite obtenir des informations sur une ressource du serveur. Lorsque le serveur reçoit et traite cette requête, il retourne les informations demandées au destinataire. La requête GET est similaire à la navigation vers une adresse dans votre navigateur Web. Lorsque vous vous rendez sur une page Web, votre navigateur émet une requête GET en coulisse. Il est ainsi en mesure d’afficher la nouvelle page HTML. Avec un appel, vous obtenez un objet Réponse.
Pour observer comment une requête GET fonctionne, ouvrez votre navigateur et ouvrez l’URI suivant : https://th-apex-http-callout.herokuapp.com/animals. Votre navigateur affiche une liste d’animaux dans un format étrange, car le service retourne la réponse au format JSON. La réponse de GET est parfois aussi au format XML.
Voici des descriptions des méthodes HTTP couramment utilisées.
Tableau 1. Quelques méthodes HTTP couramment utilisées
Méthode HTTP |
Description |
---|---|
GET |
Récupérer les données identifiées par une URL. |
POST |
Créer une ressource ou publier des données sur le serveur. |
DELETE |
Supprimer une ressource identifiée par une URL. |
PUT |
Créer ou remplacer la ressource envoyée dans le corps de la requête. |
Si vous avez un peu de temps devant vous, parcourez la liste exhaustive de toutes les méthodes HTTP qui se trouve dans la section Ressources.
En plus de la méthode HTTP, chaque requête envoie un URI, qui est l’adresse du point de terminaison au niveau duquel se trouve le service. Par exemple, le point de terminaison peut être http://www.exemple.com/api/ressource. Dans l’exemple de l’unité « Bases des appels et du HTTP », le point de terminaison est https://th-apex-http-callout.herokuapp.com/animals.
Lorsque le serveur traite la requête, il envoie un code d’état dans la réponse. Ce code d’état indique si la requête a été traitée avec succès ou si des erreurs ont été rencontrées. Si la requête aboutit, le serveur envoie le code d’état 200. Vous avez probablement déjà vu d’autres codes d’état, comme par exemple 404, qui indique qu’un fichier est introuvable, ou 500 en cas d’erreur interne du serveur.
S’il vous reste un peu de temps après avoir parcouru la liste des méthodes HTTP, consultez la liste recensant l’ensemble des codes d’état de réponse, qui se trouve dans la section Ressources. Si vous ne parvenez pas à trouver le sommeil la nuit, ces deux ressources peuvent vous aider.
Obtenir des données auprès d’un service
Il est temps à présent de mettre vos connaissances du HTTP en application en utilisant quelques appels Apex. Cet exemple envoie une requête GET à un service Web pour obtenir une liste des créatures des bois. Le service envoie la réponse au format JSON. Le format JSON comprend principalement une chaîne. Par conséquent, la classe JSONParser
intégrée le convertit en objet. Nous pouvons ensuite utiliser cet objet pour écrire le nom de chaque animal dans le journal de débogage.
Avant d’exécuter les exemples de cette unité, vous devrez autoriser l’URL du point de terminaison de l’appel, https://th-apex-http-callout.herokuapp.com, en suivant les étapes de la section Autoriser les adresses de points de terminaison.
- Ouvrez la Developer Console depuis Setup (Configuration) ().
- Dans la Developer Console, sélectionnez Débogage | Ouvrir la fenêtre d’exécution anonyme.
- Supprimez le code existant et insérez l’extrait suivant.
Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals'); request.setMethod('GET'); HttpResponse response = http.send(request); // If the request is successful, parse the JSON response. if(response.getStatusCode() == 200) { // Deserialize the JSON string into collections of primitive data types. Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody()); // Cast the values in the 'animals' key as a list List<Object> animals = (List<Object>) results.get('animals'); System.debug('Received the following animals:'); for(Object animal: animals) { System.debug(animal); } }
- Sélectionnez Ouvrir le journal, puis cliquez sur Exécuter.
- Une fois le journal de débogage ouvert, sélectionnez Debug Only (Débogage uniquement) pour afficher la sortie des instructions System.debug.
Les noms des animaux sont affichés.
Le JSON de notre exemple est plutôt simple et pratique à analyser. Pour les structures JSON plus complexes, vous pouvez utiliser JSON2Apex. Cet outil génère un solide code Apex permettant d’analyser la structure JSON. Il vous suffit de coller votre structure JSON dans cet outil, et il génère le code Apex nécessaire pour vous. Brillant !
Envoyer des données à un service
Un autre scénario d’utilisation courant d’appels HTTP consiste à envoyer des données à un service. Par exemple, lorsque vous achetez le tout dernier album de Justin Bieber ou commentez votre vidéo préféré de « Chat déguisé en requin qui poursuit un canard sur un Roomba », votre navigateur crée une requête POST pour soumettre les données. Voyons comment envoyer des données en Apex.
Cet exemple envoie une requête POST au service Web pour ajouter un nom d’animal. Le nouveau nom est ajouté au corps de la requête sous la forme d’une chaîne JSON. L’en-tête Content-Type
de la requête est défini de manière à ce que le service sache que les données envoyées sont au format JSON. Il sera alors en mesure de traiter les données de manière appropriée. Il répond en envoyant un code d’état ainsi qu’une liste de tous les animaux, qui inclut celui que vous avez ajouté. Si la requête a été traitée avec succès, le code d’état retourné est 201, car une ressource a été créée. La réponse est envoyée au journal de débogage lorsqu’un autre code d’état que 201 est retourné.
- Ouvrez la Developer Console depuis Setup (Configuration) ().
- Dans la Developer Console, sélectionnez Débogage | Ouvrir la fenêtre d’exécution anonyme.
- Supprimez le code existant et insérez l’extrait suivant :
Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals'); request.setMethod('POST'); request.setHeader('Content-Type', 'application/json;charset=UTF-8'); // Set the body as a JSON object request.setBody('{"name":"mighty moose"}'); HttpResponse response = http.send(request); // Parse the JSON response if(response.getStatusCode() != 201) { System.debug('The status code returned was not expected: ' + response.getStatusCode() + ' ' + response.getStatus()); } else { System.debug(response.getBody()); }
- Sélectionnez Ouvrir le journal, puis cliquez sur Exécuter.
- Une fois le journal de débogage ouvert, sélectionnez Debug Only (Débogage uniquement) pour afficher la sortie de l’instruction
System.debug
. Le dernier élément de la liste est « mighty moose ».
Tester les appels
Il y a une bonne et une mauvaise nouvelle concernant le test des appels. La mauvaise nouvelle, c’est que les méthodes test Apex ne prennent pas en charge les appels, et que les tests exécutant les appels échouent. Mais la bonne nouvelle, c’est que le runtime de test vous permet d’émettre des appels « fictifs ». Les appels fictifs vous offrent la possibilité de spécifier la réponse à retourner dans le cadre du test au lieu de véritablement invoquer le service Web. Vous dites globalement au runtime : « je sais ce que ce service Web retournera, donc au lieu de l’invoquer durant le test, uniquement retourner cette donnée ». L’utilisation d’appels fictifs dans vos tests vous aide à vous assurer que votre code couvre ce qu’il faut et qu’aucune ligne de code n’est ignorée du fait des appels.
Prérequis
Avant que vous ne développiez vos tests, créons une classe qui contiendra les exemples de requête GET et POST que nous avons exécutés anonymement lors de l’unité « Envoyer des données à un service ». Ces exemples sont légèrement modifiés de manière à ce que les requêtes figurent dans les méthodes et retournent des valeurs, mais ils restent pour l’essentiel identiques aux exemples précédents.
- Dans Developer Console, sélectionnez File (Fichier) | New (Nouveau) | Apex Class (Classe Apex).
- Dans Class Name (Nom de classe), saisissez
AnimalsCallouts
, puis cliquez sur OK.
- Remplacez le code généré automatiquement par la définition de classe suivante :
public class AnimalsCallouts { public static HttpResponse makeGetCallout() { Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals'); request.setMethod('GET'); HttpResponse response = http.send(request); // If the request is successful, parse the JSON response. if(response.getStatusCode() == 200) { // Deserializes the JSON string into collections of primitive data types. Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody()); // Cast the values in the 'animals' key as a list List<Object> animals = (List<Object>) results.get('animals'); System.debug('Received the following animals:'); for(Object animal: animals) { System.debug(animal); } } return response; } public static HttpResponse makePostCallout() { Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals'); request.setMethod('POST'); request.setHeader('Content-Type', 'application/json;charset=UTF-8'); request.setBody('{"name":"mighty moose"}'); HttpResponse response = http.send(request); // Parse the JSON response if(response.getStatusCode() != 201) { System.debug('The status code returned was not expected: ' + response.getStatusCode() + ' ' + response.getStatus()); } else { System.debug(response.getBody()); } return response; } }
- Appuyez sur CTRL+S pour enregistrer.
Tester un appel avec StaticResourceCalloutMock
Pour tester vos appels, utilisez des appels fictifs en implémentant une interface ou en employant des ressources statiques. Dans cet exemple, nous utilisons des ressources statiques puis une interface fictive. Les ressources statiques contiennent le corps de la réponse à retourner. Une fois encore, lorsque vous utilisez un appel fictif, la requête n’est pas envoyée au point de terminaison. Au lieu de cela, le runtime Apex runtime sait qu’il doit s’attendre à la réponse spécifiée dans la ressource statique et la retourner au lieu de contacter le point de terminaison. La méthode Test.setMock
informe le runtime que les appels fictifs sont utilisés dans la méthode test. Examinons les appels fictifs en action. Pour commencer, nous créons une ressource statique contenant une chaîne au format JSON à utiliser pour la requête GET.
- Dans la Developer Console, sélectionnez Fichier | Nouveau | Ressource statique.
- Dans Name (Nom), saisissez
GetAnimalResource
.
- Pour le type MIME, sélectionnez texte/brut, même si nous allons utiliser du JSON.
- Cliquez sur Submit (Soumettre).
- Dans l’onglet qui s’ouvre pour la ressource, insérez le contenu suivant : Assurez-vous que tout figure sur une seule ligne, sans passer à la ligne suivante. Ce contenu est ce que retourne l’appel fictif. Il s’agit d’un ensemble composé de trois créatures des bois.
{"animals":["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
({"animaux":["porc-épic pénible", "hippopotame affamé", "écureuil bavard"])
- Appuyez sur CTRL+S pour enregistrer.
Vous avez réussi à créer votre ressource statique ! Ajoutons maintenant un test de notre appel qui utilise cette ressource.
- Dans Developer Console, sélectionnez File (Fichier) | New (Nouveau) | Apex Class (Classe Apex).
- Dans Class Name (Nom de classe), saisissez
AnimalsCalloutsTest
, puis cliquez sur OK.
- Remplacez le code généré automatiquement par la définition de classe de test suivante :
@isTest private class AnimalsCalloutsTest { @isTest static void testGetCallout() { // Create the mock response based on a static resource StaticResourceCalloutMock mock = new StaticResourceCalloutMock(); mock.setStaticResource('GetAnimalResource'); mock.setStatusCode(200); mock.setHeader('Content-Type', 'application/json;charset=UTF-8'); // Associate the callout with a mock response Test.setMock(HttpCalloutMock.class, mock); // Call method to test HttpResponse result = AnimalsCallouts.makeGetCallout(); // Verify mock response is not null Assert.areNotEqual(null,result, 'The callout returned a null response.'); // Verify status code Assert.areEqual(200,result.getStatusCode(), 'The status code is not 200.'); // Verify content type Assert.areEqual('application/json;charset=UTF-8', result.getHeader('Content-Type'), 'The content type value is not expected.'); // Verify the array contains 3 items Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(result.getBody()); List<Object> animals = (List<Object>) results.get('animals'); Assert.areEqual(3, animals.size(), 'The array should only contain 3 items.'); } }
- Appuyez sur CTRL+S pour enregistrer.
- Sélectionnez Test | Always Run Asynchronously (Toujours exécuter de manière asynchrone).
Si vous ne sélectionnez pas Always Run Asynchronously (Toujours exécuter de manière asynchrone), les exécutions du test n’incluent que l’exécution synchrone d’une seule classe. Vous pouvez ouvrir des journaux à partir de l’onglet Test, uniquement pour les exécutions synchrones.
- Pour exécuter le test, sélectionnez Test | Nouvelle exécution.
- Dans la liste Test Classes, sélectionnez AnimalsCalloutsTest.
- Cliquez sur Ajouter la sélection | Exécuter.
Le résultat du test est affiché dans l’onglet Tests, sous un ID d’exécution de test. Une fois l’exécution terminée, développez-la pour observer ses détails. Maintenant, double-cliquez sur AnimalCallouts sur la page Couverture globale du code pour voir les liens couverts par vos tests.
Tester un appel avec HttpCalloutMock
Pour le test de notre appel POST, nous procédons à une implémentation de l’interface HttpCalloutMock
. Cette interface vous permet de spécifier la réponse envoyée dans la méthode respond
. Votre classe de test demande au runtime Apex d’envoyer cette réponse fictive en appelant à nouveau Test.setMock
. Pour le premier argument, transmettez HttpCalloutMock.class
. Pour le deuxième argument, transmettez une nouvelle instance de AnimalsHttpCalloutMock
, qui est votre implémentation d’interface de HttpCalloutMock
. (Nous développerons AnimalsHttpCalloutMock
dans l’exemple figurant après celui-ci.)
Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());
Ajoutez maintenant la classe implémentant l’interface HttpCalloutMock
pour intercepter l’appel. Si un appel HTTP est invoqué dans le contexte du test, il n’est pas émis. Au lieu de cela, vous recevez la réponse fictive que vous avez spécifiée dans l’implémentation de la méthode respond
dans AnimalsHttpCalloutMock
.
- Dans Developer Console, sélectionnez File (Fichier) | New (Nouveau) | Apex Class (Classe Apex).
- Dans Class Name (Nom de classe), saisissez
AnimalsHttpCalloutMock
, puis cliquez sur OK.
- Remplacez le code généré automatiquement par la définition de classe suivante :
@isTest global class AnimalsHttpCalloutMock implements HttpCalloutMock { // Implement this interface method global HTTPResponse respond(HTTPRequest request) { // Create a fake response HttpResponse response = new HttpResponse(); response.setHeader('Content-Type', 'application/json'); response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}'); response.setStatusCode(200); return response; } }
- Appuyez sur CTRL+S pour enregistrer.
Dans votre classe de test, créez la méthode testPostCallout
pour définir l’appel fictif, comme le montre l’exemple suivant. La méthode testPostCallout
invoque Test.setMock
puis la méthode makePostCallout
de la classe AnimalsCallouts
. Elle vérifie ensuite que la réponse retournée correspond à ce que vous avez spécifié dans la méthode respond
de l’implémentation fictive.
- Modifiez la classe de test
AnimalsCalloutsTest
pour ajouter une deuxième méthode de test.
- Cliquez sur l’onglet classes, puis ajoutez la méthode suivante avant le crochet de clôture.
@isTest static void testPostCallout() { // Set mock callout class Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock()); // This causes a fake response to be sent // from the class that implements HttpCalloutMock. HttpResponse response = AnimalsCallouts.makePostCallout(); // Verify that the response received contains fake values String contentType = response.getHeader('Content-Type'); Assert.isTrue(contentType == 'application/json'); String actualValue = response.getBody(); System.debug(response.getBody()); String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}'; Assert.areEqual(expectedValue, actualValue); Assert.areEqual(200, response.getStatusCode()); }
- Appuyez sur CTRL+S pour enregistrer.
- Sélectionnez Test | New Run.
- Dans la liste Test Classes, sélectionnez AnimalsCalloutsTest.
- Cliquez sur Add Selected (Ajouter la sélection) | Run (Exécuter)
Le résultat du test s’affiche dans l’onglet Tests, sous un nouvel ID d’exécution de test. Une fois l’exécution terminée, développez-la pour observer les détails des deux tests.
En savoir plus...
Informations complémentaires sur l’utilisation des appels dans les déclencheurs et en Apex asynchrone, ainsi que sur la création d’appels asynchrones.
Lorsque vous émettez un appel à partir d’une méthode, la méthode attend que le service externe lui renvoie la réponse à l’appel avant d’exécuter les lignes suivantes du code. Sinon, vous pouvez aussi placer le code de l’appel dans une méthode asynchrone annotée avec @future(callout=true)
, ou encore utiliser de l’Apex « Queueable » (pour file d’attente). De cette manière, l’appel s’exécute sur un thread distinct, et l’exécution de la méthode d’appel n’est pas bloquée.
Lorsque vous émettez un appel à partir d’un déclencheur, cet appel ne doit pas bloquer le processus du déclencheur en attendant la réponse. Pour que le déclencheur puisse émettre un appel, la méthode contenant le code de l’appel doit être annotée avec @future(callout=true)
pour s’exécuter via un thread distinct.
Ressources
-
MDN Web Docs : Méthodes de requête HTTP
-
MDN Web Docs : Codes de statut de réponse HTTP
-
Apex Developer Guide: Invoking Callouts Using Apex
-
Wikipedia : Representational state transfer
-
W3C Note : Simple Object Access Protocol (SOAP) 1.1