ABAP Cloud - HTTP Client
Wie sieht eigentlich der aktuelle HTTP Client in ABAP Cloud aus? Lasst uns einen Blick auf das neue Modell werfen.
Inhaltsverzeichnis
Zugriff auf HTTP Endpunkte wird es mit ABAP Cloud immer mehr geben, vor allem wenn du BTP Services aus deinem ABAP Code heraus aufrufen möchtest. In diesem Artikel schauen wir uns die neue freigegebene API an und beleuchten die Verwendung in der BTP und On-Premise.
Einleitung
Der HTTP Client ist schon viele Jahre Grundbestandteil eines SAP Systems und wenn du nach der Klasse CL_HTTP_CLIENT suchst, wirst du sicherlich viele Beispiele für die Nutzung finden. Schnittstellen wurden bisher aber vor allem mit RFC, IDOC oder SOAP gebaut, daher kam der HTTP Client selten zum Einsatz. Mittlerweile hat die Nutzung des alten HTTP Client auch einige Nachteile:
- Klassische Ausnahmen und deren Behandlung
- Keine saubere Trennung von Client, Request und Response
- Standardwerte sind nicht sauber gesetzt (HTTP 1.0)
Ein Beispiel für die Nutzung findest du in einem älteren Artikel von uns.
Steampunk, Public Cloud
Als Nachfolger finden wir die Klasse CL_WEB_HTTP_CLIENT_MANAGER im System. Diese ist On-Premise und in der Cloud verfügbar und kann für den Zugriff auf HTTP Endpunkte genutzt werden. In den folgenden Abschnitten gehen wir auf die Erzeugung und Verwendung ein.
Erzeugung
Bevor wir einen Endpunkt aufrufen können, müssen wir eine Verbindung herstellen. Eine Verbindung können wir über die Klasse CL_HTTP_DESTINATION_PROVIDER herstellen. Die Klasse bietet aktuell vier Methoden zur Erzeugung an:
- CREATE_BY_URL - Direkte Erzeugung über Angabe der URL. Zu Testzwecken ist die Methode sehr einfach verwendbar, sollte später aber durch eine Konfiguration ausgetauscht werden (Destination oder Arrangement).
- CREATE_BY_COMM_ARRANGEMENT - Verwendung eines Communication Arrangement. Dazu muss das System und das Arrangement konfiguriert werden.
- CREATE_BY_CLOUD_DESTINATION - Verwendung einer Destination aus dem Destination Service im BTP Subaccount.
- CREATE_BY_DESTINATION - Verwendung einer SM59 Destination zur Erzeugung des Objekts. In der Dokumentation findest du den Hinweis, dass die Methode nicht in Steampunk verwendet werden kann.
Im Beispiel erzeugen wir die Verbindung über eine URL:
DATA(lo_destination) = cl_http_destination_provider=>create_by_url(
'https://software-heroes.com/api/v1/rest-test/partner' ).
Das Request Objekt können wir uns über die Methode GET_HTTP_REQUEST holen. In diesem Fall wollen wir noch ein Header Feld setzen und den API Key der Schnittstelle versorgen:
lo_client->get_http_request( )->set_header_field( i_name = 'Swh-API-Key'
i_value = c_api_key ).
Lesen
Möchten wir nun Daten lesen, erzeugen wir uns im ersten Schritt einen Client und führen dann den EXECUTE mit der Methode GET aus. Als Ergebnis erhalten wir ein Objekt mit dem Ergebnis der Anfrage zurück:
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>get ).
Um nun an die Inhalte der Anfrage zu kommen, können wir die verschiedenen Methoden des RESPONSE Objekts verwenden.
DATA(ld_content_type) = lo_response->get_content_type( ).
DATA(ls_status) = lo_response->get_status( ).
DATA(ld_text) = lo_response->get_text( ).
Schauen wir uns dazu die Konsole an, erhalten wir vom Server eine entsprechende Antwort:
Anlegen
In diesem zweiten Beispiel legen wir einen Datensatz per POST Request über die API an. Im ersten Schritt erzeugen wir einen neuen CLIENT, wie bereits im Beispiel zuvor. Als nächstes holen wir uns das Request Objekt und können Header Felder oder andere Eigenschaften der Anfrage hinzufügen:
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_header_field( i_name = 'Swh-API-Key'
i_value = c_api_key ).
Nun konfigurieren wir die Payload, die wir dem Service mitgeben wollen. In diesem Fall erzeugen wir das JSON einfach direkt im Code und übergeben es an die Anfrage.
lo_request->set_content_type( 'application/json' ).
lo_request->set_text( |{| &
| "NAME": "VW",| &
| "DESCRIPTION": "New cars",| &
| "STATUS": "A",| &
| "PRICE": 723.99,| &
| "CURRENCY": "USD"| &
|}| ).
Die Ausführung sieht nun so ähnlich aus, wie beim Lesen, allerdings geben wir eine andere HTTP Methode mit. Über POST kannst du typischerweise bei REST Schnittstellen einen neuen Datensatz anlegen:
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post ).
Weitere Beispiele
Weitere Beispiele findest du in der SAP Community, der SAP Help oder auf anderen Blogs. Im unteren Teil haben wir einen Artikel in der SAP Community verlinkt, wo du weitere Details findest.
On-Premise, Private Cloud
In diesem Abschnitt findest du Informationen zur Verwendung in einem On-Premise System.
Provider
Aktuell ist allerdings die Klasse CL_WEB_HTTP_CLIENT_PROVIDER nicht für die Private Cloud und On-Premise verfügbar. Über die nicht freigegebene Klasse CL_OUTBOUND_PROVIDER_HTTP kann eine Destination erzeugt werden, dabei stehen dir aktuell die folgenden Methoden zur Nutzung zur Verfügung:
- CREATE_BY_DESTINATION - Verwendung einer SM59 Destination zur Erzeugung des Verbindungs-Objekts.
Hinweis: Damit du die Klasse in ABAP Cloud (TIER-1) verwenden kannst, musst du dir einen TIER-2 Wrapper schreiben. Hierzu findest du auch weitere Informationen im Dokument "ABAP Cloud API Enablement Guidelines" (Punkt 2.3.1.3 - Connectivity).
Verwendung
Zuerst müssen wir wieder eine Verbindung erzeugen, dies geht aktuell nur über eine konfigurierte SM59 Verbindung. In diesem Beispiel verwenden wir die Originalklasse, für ABAP Cloud benötigst du allerdings einen Wrapper.
" You need a wrapper for this class
DATA(lo_destination) = cl_outbound_provider_http=>create_by_destination( c_destination ).
Nun können wir wieder die verschiedenen Optionen zum Aufruf der API implementieren und per EXECUTE die Anfrage ausführen.
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>get ).
Planung
Laut Planung kommt mit dem S/4HANA 2025 Release eine Nachfolger API, die in allen Umgebungen genutzt werden kann. Wie die API aussehen wird, können wir heute noch nicht sagen. Da es sich hierbei um eine Planung handelt, solltest du erst einmal auf den Wrapper setzen.
Utility
Für die HTTP Utility Klasse CL_HTTP_UTILITY steht nun die freigegebene API CL_WEB_HTTP_UTILITY zur Verfügung. Wenn wir uns die Klasse anschauen, sehen wir schnell, dass diese ein Wrapper für die alte Klasse ist. Immerhin wurden die klassischen Ausnahmen durch klassenbasierte Ausnahmen ersetzt und etwas modernisiert. Zum Funktionsumfang der Klasse gehört:
- Konvertierung UTF-8 - Damit können Strings konvertiert werden, von UTF-8 auf das interne Format und in beide Richtungen.
- Konvertierung BASE64 - Hier gibt es die jeweiligen Varianten für String und XString.
- URL Funktionen - Escapen, Prüfung auf Richtigkeit und Normalisierung von URLs findest du weiterhin.
Hinweis: Es werden nicht alle Funktionalitäten der Originalklasse zur Verfügung gestellt und die Klasse kann nicht so einfach in Unit Tests gemockt werden.
Vollständiges Beispiel
Hier findest du noch einmal den vollständigen Code aus dem gezeigten Beispiel. Diese läuft aktuell nur im ABAP Environment oder der S/4HANA Public Cloud.
CLASS zcl_bs_demo_http_client DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
CONSTANTS c_api_key TYPE string VALUE ``.
CONSTANTS c_api_endpoint TYPE string VALUE `https://software-heroes.com/api/v1/rest-test/partner`.
METHODS create_new_data
IMPORTING i_out TYPE REF TO if_oo_adt_classrun_out.
METHODS read_data
IMPORTING i_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_http_client IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
* read_data( out ).
create_new_data( out ).
ENDMETHOD.
METHOD read_data.
TRY.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( c_api_endpoint ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
lo_client->get_http_request( )->set_header_field( i_name = 'Swh-API-Key'
i_value = c_api_key ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>get ).
i_out->write( lo_response->get_content_type( ) ).
i_out->write( lo_response->get_status( ) ).
i_out->write( lo_response->get_text( ) ).
CATCH cx_root INTO DATA(lo_error).
i_out->write( lo_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
METHOD create_new_data.
TRY.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( c_api_endpoint ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_header_field( i_name = 'Swh-API-Key'
i_value = c_api_key ).
lo_request->set_content_type( 'application/json' ).
lo_request->set_text( |{| &
| "NAME": "VW",| &
| "DESCRIPTION": "New cars",| &
| "STATUS": "A",| &
| "PRICE": 723.99,| &
| "CURRENCY": "USD"| &
|}| ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post ).
i_out->write( lo_response->get_content_type( ) ).
i_out->write( lo_response->get_status( ) ).
i_out->write( lo_response->get_text( ) ).
CATCH cx_root INTO DATA(lo_error).
i_out->write( lo_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.
Für On-Premise und Private Cloud findest du hier das Beispiel mit der nicht freigegebenen Klasse:
CLASS zcl_bs_demo_http_client DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
CONSTANTS c_destination TYPE rfcdest VALUE ''.
ENDCLASS.
CLASS zcl_bs_demo_http_client IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
TRY.
" You need a wrapper for this class
DATA(lo_destination) = cl_outbound_provider_http=>create_by_destination( c_destination ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>get ).
out->write( lo_response->get_content_type( ) ).
out->write( lo_response->get_status( ) ).
out->write( lo_response->get_text( ) ).
CATCH cx_root INTO DATA(lo_error).
out->write( lo_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.
Fazit
Aktuell ist die Situation mit dem HTTP Client noch nicht vollständig geklärt und es bedarf etwas Nacharbeit beim Zugriff auf die passende API, der Client kann allerdings schon genutzt werden. Immerhin steht bereits die Utility Klasse im System zur Verfügung und kann genutzt werden.
Weitere Informationen:
SAP Community - ABAP & HTTP
SAP Community - Use New Client