Erfassen Sie Ihre Fortschritte
Trailhead-Startseite
Trailhead-Startseite

Apex-REST-Callouts

Lernziele

Nachdem Sie dieses Modul abgeschlossen haben, sind Sie in der Lage, die folgenden Aufgaben auszuführen:
  • Ausführen eines Callouts zum Empfangen von Daten von einem externen Dienst
  • Ausführen eines Callouts zum Senden von Daten an einen externen Dienst
  • Testen von Callouts mithilfe simulierter Callouts

HTTP und Callouts: Grundlagen

REST-Callouts basieren auf HTTP. Um die Funktionsweise von Callouts zu verstehen, ist es hilfreich, sich mit verschiedenen Aspekten von HTTP vertraut zu machen. Jede Calloutanforderung ist einer HTTP-Methode und einem Endpunkt zugeordnet. Die HTTP-Methode gibt an, welche Art von Aktion gewünscht ist.
Apex-Callouts an einen externen Dienst

Die einfachste Anforderung ist eine GET-Anforderung (GET ist eine HTTP-Methode). Eine GET-Anforderung bedeutet, dass der Absender Informationen zu einer Ressource vom Server abrufen möchte. Nachdem der Server diese Anforderung empfangen und verarbeitet hat, gibt er die Anforderungsinformationen an den Empfänger zurück. Eine GET-Anforderung ähnelt dem Navigieren zu einer Adresse im Browser. Wenn Sie eine Webseite besuchen, erfüllt der Browser im Hintergrund eine GET-Anforderung. Das Ergebnis der Navigation im Browser ist eine neue HTML-Seite, die eingeblendet wird. Bei einem Callout ist das Ergebnis das Antwortobjekt.

Zum Veranschaulichen, wie eine GET-Anforderung funktioniert, öffnen Sie Ihren Browser und navigieren zum folgenden URL: https://th-apex-http-callout.herokuapp.com/animals. Ihr Browser zeigt eine Liste mit Tieren in einem sonderbaren Format an, da der Dienst die Antwort in einem Format namens JSON zurückgibt. Mitunter wird eine GET-Antwort auch als XML formatiert.

Es folgen Beschreibungen gängiger HTTP-Methoden.

Tabelle 1. Gängige HTTP-Methoden
HTTP-Methode Beschreibung
GET Abrufen von Daten, die von einem URL bestimmt werden.
POST Erstellen einer Ressource oder Senden von Daten zum Server.
DELETE Löschen einer Ressource, die von einem URL bestimmt wird.
PUT Erstellen oder Ersetzen der Ressource, die im Anforderungstext gesendet wird.

Wenn Sie etwas Zeit übrig haben, gehen Sie im Abschnitt Ressourcen die umfassende Liste aller HTTP-Methoden durch.

Zusätzlich zur HTTP-Methode legt jede Anforderung einen URI fest, der die Endpunktadresse darstellt, an dem sich der Service befindet. Ein Endpunkt kann beispielsweise http://www.example.com/api/resource heißen. Im Beispiel in der Einheit HTTP und Callouts: Grundlagen heißt der Endpunkt https://th-apex-http-callout.herokuapp.com/animals.

Nachdem der Server diese Anforderung verarbeitet hat, sendet er in der Antwort einen Statuscode. Der Statuscode gibt an, ob die Anforderung erfolgreich verarbeitet wurde oder ob Fehler aufgetreten sind. Bei einer erfolgreichen Anforderung sendet der Server den Statuscode 200. Sie haben bestimmt schon andere Statuscodes kennengelernt, wie z. B. 404 (Datei nicht gefunden) oder 500 (interner Serverfehler).

Wenn Sie noch etwas Zeit übrig haben, nachdem Sie sich mit der umfassenden Liste aller HTTP-Methoden befasst haben, sehen Sie sich im Abschnitt Ressourcen die Liste mit allen Antwortstatuscodes an. Wenn Sie nachts mal nicht einschlafen können, sind diese beiden Ressourcen hilfreich.

Hinweis

Hinweis

Zusätzlich zum Endpunkt und zur HTTP-Methode können Sie andere Eigenschaften für eine Anforderung festlegen. Eine Anforderung kann beispielsweise Header enthalten, die weitere Informationen zur Anforderung bereitstellen, z. B. den Inhaltstyp. Eine Anforderung kann auch Daten enthalten, die an den Service gesendet werden sollen, z. B. bei POST-Anforderungen. In einem späteren Schritt sehen Sie ein Beispiel einer POST-Anforderung. Eine POST-Anforderung ähnelt dem Klicken auf eine Schaltfläche auf einer Webseite zum Übermitteln von Formulardaten. Bei einem Callout senden Sie Daten als Teil des Anforderungstexts, anstatt die Daten manuell auf einer Webseite einzugeben.

Abrufen von Daten aus einem Dienst

Sie können nun Ihre neuen HTTP-Kenntnisse für einige Apex-Callouts nutzen. Dieses Beispiel sendet eine GET-Anforderung an einen Webservice, um eine Liste von Waldtieren abzurufen. Der Service sendet die Antwort im JSON-Format. JSON ist im Wesentlichen eine Zeichenfolgendarstellung, die deshalb von der integrierten JSONParser-Klasse in ein Objekt umgewandelt wird. Sie können dann mithilfe dieses Objekts den Namen jedes Tieres in das Debug-Protokoll schreiben.

Ehe Sie die Beispiele in dieser Einheit ausführen, müssen Sie den Endpunkt-URL des Callouts, https://th-apex-http-callout.herokuapp.com, anhand der Schritte im Abschnitt Autorisieren von Endpunktadressen autorisieren.

  1. Klicken Sie auf das Setup-Zahnradsymbol (Setup-Zahnradsymbol) und wählen Sie Entwicklerkonsole aus.
  2. Klicken Sie in der Entwicklerkonsole auf Debug | Open Execute Anonymous Window.
  3. Entfernen Sie den vorhandenen Code und fügen Sie den folgenden Codeabschnitt ein.
    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. Wählen Sie Open Log aus und klicken Sie auf Execute.
  5. Wählen Sie nach Öffnen des Debug-Protokolls Debug Only aus, um die Ausgabe der System.debug-Anweisungen anzuzeigen.
    Die Namen der Tiere werden angezeigt.

Der JSON-Code unseres Beispiels ist relativ einfach und leicht zu analysieren. Für komplexere JSON-Strukturen können Sie JSON2Apex verwenden. Dieses Tool erzeugt stark typisierten Apex-Code für die Analyse einer JSON-Struktur. Fügen Sie einfach den JSON-Code ein, woraufhin das Tool den benötigten Apex-Code für Sie generiert. Brillant!

Senden von Daten an einen Dienst

Ein weiterer gängiger Anwendungsfall für HTTP-Callouts ist das Senden von Daten an einen Dienst. Wenn Sie beispielsweise das neueste Album von Justin Bieber kaufen oder Ihr Lieblingsvideo "Katze im Hai-Kostüm jagt Ente auf einem Roomba-Staubsauger" kommentieren, erstellt Ihr Browser eine POST-Anforderung zum Übermitteln von Daten. Lassen Sie uns nun untersuchen, wie wir Daten in Apex senden.

Dieses Beispiel sendet eine POST-Anforderung an den Webservice, um einen Tiernamen hinzuzufügen. Der neue Name wird dem Anforderungstext als JSON-Zeichenfolge hinzugefügt. Der Anforderungsheader Content-Type ist so festgelegt, dass der Service informiert wird, dass die gesendeten Daten das JSON-Format haben, damit sie ordnungsgemäß verarbeitet werden können. Der Service antwortet mit dem Senden eines Statuscodes und einer Liste aller Tiere einschließlich desjenigen, das Sie hinzugefügt haben. Bei erfolgreicher Verarbeitung der Anforderung wird der Statuscode 201 zurückgegeben, weil eine Ressource erstellt wurde. Bei einer anderen Rückgabe als 201 wird die Antwort an das Debug-Protokoll gesendet.

  1. Klicken Sie auf das Setup-Zahnradsymbol (Setup-Zahnradsymbol) und wählen Sie Entwicklerkonsole aus.
  2. Klicken Sie in der Entwicklerkonsole auf Debug | Open Execute Anonymous Window.
  3. Löschen Sie vorhandenen Code und fügen Sie den folgenden Codeabschnitt ein.
    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. Wählen Sie Open Log aus und klicken Sie auf Execute.
  5. Wählen Sie nach Öffnen des Debug-Protokolls Debug Only aus, um die Ausgabe der System.debug-Anweisung anzuzeigen. Der letzte Eintrag in der Liste der Tiere ist "mighty moose".

Testen von Callouts

Hinsichtlich des Testens von Callouts gibt es gute und schlechte Nachrichten. Die schlechte Nachricht ist, dass Apex-Testmethoden keine Callouts unterstützen, sodass Tests, die Callouts durchführen, fehlschlagen. Die gute Nachricht ist, dass die Testlaufzeit Ihnen ermöglicht, Callouts zu simulieren. Simulierte Callouts erlauben Ihnen das Angeben der beim Test zurückzugebenden Antwort, ohne den Webservice tatsächlich aufzurufen. Sie informieren die Laufzeit im Wesentlichen wie folgt: "Ich weiß, was dieser Webservice zurückgeben wird, weshalb bei Tests diese Daten zurückgegeben werden sollen, anstatt ihn aufzurufen." Das Verwenden simulierter Callouts in Ihren Tests hilft sicherzustellen, dass Sie eine ausreichende Codeabdeckung erreichen und dass aufgrund von Callouts keine Codezeilen übersprungen werden.

Voraussetzungen

Lassen Sie uns, bevor Sie Ihre Tests schreiben, eine Klasse mit Beispielen von GET- und POST-Anforderungen erstellen, die wir in der Einheit Senden von Daten an einen Service anonym ausgeführt haben. Die Beispiele wurden geringfügig so geändert, dass sich die Anforderungen in Methoden und Rückgabewerten befinden. Sie sind jedoch im Wesentlichen mit den vorherigen Beispielen identisch.

  1. Klicken Sie in der Entwicklerkonsole auf File | New | Apex Class.
  2. Geben Sie als Klassennamen AnimalsCallouts ein und klicken Sie auf OK.
  3. Ersetzen Sie den automatisch generierten Code durch die folgende Klassendefinition.
    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. Drücken Sie STRG+S, um zu speichern.

Testen eines Callouts mit StaticResourceCalloutMock

Verwenden Sie zum Testen Ihrer Callouts simulierte Callouts, indem Sie entweder eine Schnittstelle implementieren oder statische Ressourcen nutzen. Bei diesem Beispiel nutzen wir statische Ressourcen und später eine simulierte Schnittstelle. Die statische Ressource enthält den zurückzugebenden Antworttext. Bei Verwenden eines simulierten Callouts wird die Anforderung nicht zum Endpunkt gesendet. Stattdessen weiß die Apex-Laufzeit, dass sie stattdessen die in der statischen Ressource angegebene Antwort nachschlagen und zurückgeben soll. Die Test.setMock-Methode informiert die Laufzeit, dass in der Testmethode simulierte Callouts verwendet werden. Sehen wir uns simulierte Callouts in Aktion an. Zuerst erstellen wir eine statische Ressource mit einer Zeichenfolge im JSON-Format, die für die GET-Anforderung verwendet wird.

  1. Klicken Sie in der Entwicklerkonsole auf File | New | Static Resource.
  2. Geben Sie als Namen GetAnimalResource ein.
  3. Wählen Sie als MIME-Typ text/plain, selbst wenn wir JSON verwenden.
  4. Klicken Sie auf Submit.
  5. Fügen Sie auf der Registerkarte, die für die Ressource geöffnet wird, folgenden Inhalt ein. Vergewissern Sie sich, dass sich alles in einer Zeile befindet und kein Umbruch in die nächste Zeile erfolgt. Dieser Inhalt wird vom simulierten Callout zurückgegeben. Es handelt sich um ein Array von Waldtieren.
    {"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
  6. Drücken Sie STRG+S, um zu speichern.

Sie haben Ihre statische Ressource erfolgreich erstellt! Lassen Sie uns nun einen Test für unseren Callout hinzufügen, der diese Ressource verwendet.

  1. Klicken Sie in der Entwicklerkonsole auf File | New | Apex Class.
  2. Geben Sie als Klassennamen AnimalsCalloutsTest ein und klicken Sie auf OK.
  3. Ersetzen Sie den automatisch generierten Code durch die folgende Testklassendefinition.
    @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. Drücken Sie STRG+S, um zu speichern.
  5. Wählen Sie Test | Always Run Asynchronously aus. Wenn Sie "Always Run Asynchronously" nicht aktivieren, werden Testläufe, die nur einer Klasse enthalten, synchron ausgeführt. Sie können auf der Registerkarte "Tests" Protokolle nur für synchrone Testläufe öffnen.
  6. Um diesen Test auszuführen, klicken Sie auf Test | New Run.
  7. Wählen Sie in der Liste "Test Classes" AnimalsCalloutsTest aus.
  8. Klicken Sie auf Add Selected | Run.

Das Testergebnis wird auf der Registerkarte "Tests" unter einer Testlauf-ID angezeigt. Erweitern Sie nach Abschluss des Tests den Testlauf, um Details anzuzeigen. Doppelklicken Sie nun im Bereich "Overall Code Coverage" auf AnimalCallouts, um zu prüfen, welche Zeilen von Ihren Tests abgedeckt werden.

Testen eines Callouts mit HttpCalloutMock

Zum Testen Ihres POST-Callouts stellen wir eine Implementierung der HttpCalloutMock-Schnittstelle zur Verfügung. Die Schnittstelle ermöglicht Ihnen das Angeben der Antwort, die in der respond-Methode gesendet wird. Ihre Testklasse weist die Apex-Laufzeit an, diese simulierte Antwort zu senden, indem Test.setMock erneut aufgerufen wird. Übergeben Sie für das erste Argument HttpCalloutMock.class. Übergeben Sie für das zweite Argument eine neue Instanz von AnimalsHttpCalloutMock. Dies ist Ihre Schnittstellenimplementierung von HttpCalloutMock. (AnimalsHttpCalloutMock schreiben wir in dem Beispiel, das auf dieses folgt.)

Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

Fügen Sie nun die Klasse hinzu, die die HttpCalloutMock-Schnittstelle implementiert, um den Callout abzufangen. Wenn ein HTTP-Callout im Testkontext aufgerufen wird, erfolgt dieser nicht. Stattdessen erhalten Sie die simulierte Antwort, die Sie in der Implementierung der respond-Methode in AnimalsHttpCalloutMock angeben.

  1. Klicken Sie in der Entwicklerkonsole auf File | New | Apex Class.
  2. Geben Sie als Klassennamen AnimalsHttpCalloutMock ein und klicken Sie auf OK.
  3. Ersetzen Sie den automatisch generierten Code durch die folgende Klassendefinition.
    @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. Drücken Sie STRG+S, um zu speichern.

Erstellen Sie in Ihrer Testklasse die testPostCallout-Methode zum Festlegen des simulierten Callouts (siehe das nächste Beispiel). Die testPostCallout-Methode ruft Test.setMock und dann die makePostCallout-Methode in der AnimalsCallouts-Klasse auf. Sie überprüft dann, ob die zurückgegebene Antwort derjenigen entspricht, die Sie in der "respond"-Methode der Implementierung der Simulation angegeben haben.

  1. Ändern Sie die Testklasse AnimalsCalloutsTest, indem Sie eine zweite Testmethode hinzufügen. Klicken Sie auf die Registerkarte "Class" und fügen Sie vor der schließenden Klammer die folgende Methode hinzu.
    @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. Drücken Sie STRG+S, um zu speichern.
  3. Wählen Sie Test | New Run aus.
  4. Wählen Sie in der Liste "Test Classes" AnimalsCalloutsTest aus.
  5. Klicken Sie auf Add Selected | Run.

    Das Testergebnis wird auf der Registerkarte "Tests" unter einer neuen Testlauf-ID angezeigt. Blenden Sie nach Abschluss des Tests den Testlauf ein, um Details zu beiden Tests anzuzeigen.

Weitere Infos

Erfahren Sie, wie Callouts in Auslösern und asynchronem Apex-Code verwendet werden und asynchrone Callouts erfolgen.

Wenn aus einer Methode ein Callout erfolgt, wartet die Methode, bis der externe Service die Antwort auf den Callout zurücksendet, ehe nachfolgende Codezeilen ausgeführt werden. Alternativ können Sie den Calloutcode in einer asynchronen Methode platzieren, die mit der Anmerkung @future(callout=true) versehen wird, oder Queueable Apex verwenden. Auf diese Weise wird der Callout in einem getrennten Thread ausgeführt und die Ausführung der aufrufenden Methode nicht blockiert.

Wenn ein Callout aus einem Auslöser erfolgt, darf der Callout den Auslöserprozess nicht blockieren, während auf die Antwort gewartet wird. Damit der Auslöser den Callout auslösen kann, muss die Methode mit dem Calloutcode mit der Anmerkung @future(callout=true) versehen werden, damit die Ausführung in einem getrennten Thread erfolgt.

Ressourcen

Hinweis

Hinweis

Nicht vergessen: Dieses Modul bezieht sich auf Salesforce Classic. Wenn Sie Ihre Übungs-Organisation starten, wechseln Sie zu Salesforce Classic, um diese Aufgabe abzuschließen.