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
Mit Trail Together einem Dozenten folgen
Möchten Sie bei diesem Schritt einem Experten folgen? Schauen Sie sich dieses Video an, das Teil der Reihe "Trail Together" auf Trailhead Live ist.
(Dieser Clip beginnt bei Minute 07:54, für den Fall, dass Sie zurückspringen und den Anfang des Schritts noch einmal sehen möchten.)
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.
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 URI: 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.
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.
- Klicken Sie auf das Setup-Zahnradsymbol () und wählen Sie "Developer Console (Entwicklerkonsole)" aus.
- Klicken Sie in der Entwicklerkonsole auf Debug | Open Execute Anonymous Window.
- 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); } }
- Wählen Sie Open Log aus und klicken Sie auf Execute.
- 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 ein JSON2Apex-Tool verwenden. Fügen Sie einfach den JSON-Code ein, woraufhin das Tool den benötigten Apex-Code für Sie generiert. Diese Tools erzeugen stark typisierten Apex-Code für die Analyse einer JSON-Struktur. 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.
- Klicken Sie auf das Setup-Zahnradsymbol () und wählen Sie "Developer Console (Entwicklerkonsole)" aus.
- Klicken Sie in der Entwicklerkonsole auf Debug | Open Execute Anonymous Window.
- 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()); }
- Wählen Sie Open Log aus und klicken Sie auf Execute.
- Wählen Sie nach dem Öffnen des Debug-Protokolls Debug Only (Nur debuggen) 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.
- Klicken Sie in der Developer Console auf File (Datei) | New (Neu) | Apex Class (Apex-Klasse).
- Geben Sie als Klassennamen
AnimalsCallouts
ein und klicken Sie auf OK.
- 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; } }
- 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.
- Klicken Sie in der Entwicklerkonsole auf File | New | Static Resource.
- Geben Sie als Namen
GetAnimalResource
ein.
- Wählen Sie als MIME-Typ text/plain, selbst wenn wir JSON verwenden.
- Klicken Sie auf Submit (Senden).
- Fügen Sie auf der Registerkarte, die für die Ressource geöffnet wird, folgenden Inhalt ein. Dieser Inhalt wird vom simulierten Callout zurückgegeben. Es handelt sich um ein Array von Waldtieren.
{"animals":["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
[Vergewissern Sie sich, dass sich alles in einer Zeile befindet und kein Umbruch in die nächste Zeile erfolgt.]
- 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.
- Klicken Sie in der Developer Console auf File (Datei) | New (Neu) | Apex Class (Apex-Klasse).
- Geben Sie als Klassennamen
AnimalsCalloutsTest
ein und klicken Sie auf OK.
- 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 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.'); } }
- Drücken Sie STRG+S, um zu speichern.
- 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 asynchrone Testläufe öffnen.
- Um diesen Test auszuführen, klicken Sie auf Test | New Run.
- Wählen Sie in der Liste "Test Classes" AnimalsCalloutsTest aus.
- 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.
- Klicken Sie in der Developer Console auf File (Datei) | New (Neu) | Apex Class (Apex-Klasse).
- Geben Sie als Klassennamen
AnimalsHttpCalloutMock
ein und klicken Sie auf OK.
- 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; } }
- 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.
- Ä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'); 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()); }
- Drücken Sie STRG+S, um zu speichern.
- Wählen Sie Test | New Run aus.
- Wählen Sie in der Liste "Test Classes" AnimalsCalloutsTest aus.
- 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
- MDN Web Docs: HTTP-Anfragemethoden
- MDN Web Docs: HTTP-Antwort-Codes
- Apex Developer Guide: Invoking Callouts Using Apex
- Wikipedia: Representational State Transfer (REST)
- W3C-Hinweis: Simple Object Access Protocol (SOAP) 1.1