Suivez votre progression
Accueil Trailhead
Accueil Trailhead

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.

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é.
Appels Apex vers un service externe

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.

Table 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, naviguez vers 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 est une réussite, 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 de tous les 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.

Remarque

Remarque

En plus du point de terminaison et de la méthode HTTP, vous pouvez définir d’autres propriétés pour une requête. Par exemple, une requête peut contenir des en-têtes fournissant des informations supplémentaires sur la requête, notamment sur le type de contenu. Une requête peut aussi contenir des données à envoyer au service. C’est par exemple le cas des requêtes POST. Vous verrez un exemple de requête POST ultérieurement. La requête POST revient à cliquer sur un bouton d’une page Web pour soumettre des données de formulaire. Avec un appel, vous envoyez les données dans le corps de la requête au lieu de les entrer manuellement sur une page Web.

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.

  1. Ouvrez la Developer Console depuis l’icône Configuration (Icône d’engrenage Configuration).
  2. Dans la Developer Console, sélectionnez Débogage | Ouvrir la fenêtre d’exécution anonyme.
  3. 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);
        }
    }
  4. Sélectionnez Open Log, puis cliquez sur Execute.
  5. Une fois le journal de débogage ouvert, sélectionnez Debug Only 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é.

  1. Ouvrez la Developer Console depuis l’icône Configuration (Icône d’engrenage Configuration).
  2. Dans la Developer Console, sélectionnez Débogage | Ouvrir la fenêtre d’exécution anonyme.
  3. 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());
    }
  4. Sélectionnez Ouvrir le journal, puis cliquez sur Exécuter.
  5. Une fois le journal de débogage ouvert, sélectionnez 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 de développer 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.

  1. Dans la Developer Console, sélectionnez Fichier | Nouveau | Classe Apex.
  2. Saisissez le nom de classe AnimalsCallouts, puis cliquez sur OK.
  3. 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;
        }        
    
    }
  4. 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.

  1. Dans la Developer Console, sélectionnez Fichier | Nouveau | Ressource statique.
  2. Saisissez le nom GetAnimalResource.
  3. Pour le type MIME, sélectionnez texte/brut, même si nous allons utiliser du JSON.
  4. Cliquez sur Soumettre.
  5. 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. C’est un tableau contenant trois créatures des bois.
    {"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
  6. 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.

  1. Dans la Developer Console, sélectionnez Fichier | Nouveau | Classe Apex.
  2. Saisissez le nom AnimalsCalloutsTest, puis cliquez sur OK.
  3. 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
            System.assertNotEquals(null,result,
                'The callout returned a null response.');
            // Verify status code
            System.assertEquals(200,result.getStatusCode(),
              'The status code is not 200.');
            // Verify content type   
            System.assertEquals('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');
            System.assertEquals(3, animals.size(),
              'The array should only contain 3 items.');          
        }   
    
    }
  4. Appuyez sur CTRL+S pour enregistrer.
  5. Sélectionnez Test | Toujours exécuter de manière aynchrone. Si vous ne sélectionnez pas 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.
  6. Pour exécuter le test, sélectionnez Test | Nouvelle exécution.
  7. Dans la liste Test Classes, sélectionnez AnimalsCalloutsTest.
  8. 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.

  1. Dans la Developer Console, sélectionnez Fichier | Nouveau | Classe Apex.
  2. Saisissez le nom AnimalsHttpCalloutMock, puis cliquez sur OK.
  3. 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; 
        }
    }
  4. 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 est ce que vous avez spécifié dans la méthode respond de l’implémentation fictive.

  1. Modifiez la classe de test AnimalsCalloutsTest pour ajouter une deuxième méthode 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');
        System.assert(contentType == 'application/json');
        String actualValue = response.getBody();
        System.debug(response.getBody());
        String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, response.getStatusCode());
    }
  2. Appuyez sur CTRL+S pour enregistrer.
  3. Sélectionnez Test | Nouvelle exécution.
  4. Dans la liste Test Classes, sélectionnez AnimalsCalloutsTest.
  5. Cliquez sur Ajouter la sélection | 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

Remarque

Remarque

N’oubliez pas que ce module est conçu pour Salesforce Classic. Lorsque vous lancez votre organisation d’exercice, basculez vers Salesforce Classic pour relever ce défi.