Service Consumption Model - Externe APIs typsicher konsumieren

kategorie
Integration
Veröffentlicht
autor
Johannes

Das Service Consumption Model (SCM) ist der empfohlene Weg, externe REST- und OData-APIs in ABAP Cloud typsicher zu konsumieren. Im Gegensatz zum direkten HTTP-Client-Aufruf generiert das SCM automatisch ABAP-Proxyklassen und Datentypen aus der API-Spezifikation - das spart Entwicklungszeit und verhindert Fehler.

Übersicht: SCM vs. HTTP Client

AspektService Consumption ModelHTTP Client direkt
TypsicherheitVollständig - generierte TypenKeine - manuelle JSON-Verarbeitung
AufwandEinmalig: Import + KonfigurationJeder Aufruf: Request bauen, Response parsen
WartbarkeitHoch - API-Änderungen sichtbarNiedrig - Fehler erst zur Laufzeit
FlexibilitätEingeschränkt auf definierte APIsMaximale Flexibilität
Use CaseStabile externe APIs (OData, OpenAPI)Ad-hoc-Aufrufe, dynamische APIs

Wann welchen Ansatz wählen?

Service Consumption Model nutzen wenn:

  • Du eine stabile OData- oder REST-API konsumierst
  • Die API eine EDMX- oder OpenAPI-Spezifikation hat
  • Typsicherheit und Wartbarkeit wichtig sind
  • Mehrere API-Operationen genutzt werden

HTTP Client direkt nutzen wenn:

  • Die API keine formale Spezifikation hat
  • Du nur wenige, einfache Aufrufe brauchst
  • Die API sich häufig ändert
  • Du maximale Kontrolle über Request/Response benötigst

Für Details zum direkten HTTP-Client-Ansatz siehe ABAP HTTP Client: REST-APIs aufrufen.

Unterstützte API-Formate

Das Service Consumption Model unterstützt drei Formate:

┌─────────────────────────────────────────────────────────────────────────┐
│ Service Consumption Model - Unterstützte Formate │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ OData V2 (EDMX) │ │ OData V4 (EDMX) │ │ OpenAPI 3.0 │ │
│ │ │ │ │ │ (JSON/YAML) │ │
│ │ - SAP Standard │ │ - Modernes OData │ │ - REST APIs │ │
│ │ - S/4HANA APIs │ │ - RAP Services │ │ - Externe APIs │ │
│ │ - SuccessFactors │ │ - BTP Services │ │ - Cloud APIs │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
│ │
│ Import in ADT: │
│ File → New → Other ABAP Repository Object │
│ → Connectivity → Service Consumption Model │
└─────────────────────────────────────────────────────────────────────────┘

Schritt-für-Schritt: SCM erstellen

1. API-Spezifikation beschaffen

Zuerst benötigst du die EDMX- oder OpenAPI-Datei der Ziel-API:

OData-APIs (EDMX):

# SAP API Business Hub
https://api.sap.com → API auswählen → API Specification → Download EDMX
# Aus laufendem SAP-System
GET https://<host>/sap/opu/odata/sap/<SERVICE_NAME>/$metadata

REST-APIs (OpenAPI):

# Öffentliche APIs
https://api.example.com/openapi.json
https://api.example.com/swagger.json
# SAP BTP Services
https://api.sap.com → Service auswählen → Download OpenAPI spec

2. Service Consumption Model anlegen

In ADT (ABAP Development Tools):

  1. Rechtsklick auf Package → NewOther ABAP Repository Object
  2. Kategorie: ConnectivityService Consumption Model
  3. Wizard ausfüllen:
FeldBeispielwertBeschreibung
NameZSCM_WEATHER_APITechnischer Name
DescriptionWeather API ConsumerKurzbeschreibung
Remote Consumption ModeOData oder Web ServiceAPI-Typ
  1. Metadaten-Datei importieren (EDMX, OpenAPI JSON, oder WSDL)
  2. Finish - ADT generiert alle Artefakte

3. Generierte Artefakte verstehen

Nach dem Import findest du folgende Objekte:

ZSCM_WEATHER_API (Service Consumption Model)
├── ZSCM_WEATHER_API_PROXY (Proxy-Klasse)
│ ├── get_weather_forecast() " Operation aus API
│ ├── get_current_weather() " Operation aus API
│ └── create_alert() " Operation aus API
├── ZS_WEATHER_FORECAST (Struktur)
│ ├── location TYPE string
│ ├── temp_min TYPE decfloat16
│ └── temp_max TYPE decfloat16
├── ZS_CURRENT_WEATHER (Struktur)
│ └── ...
└── ZCX_WEATHER_API (Exception-Klasse)
└── Fachliche/technische Fehler

Code-Beispiel: API-Aufruf mit SCM-Proxy

Voraussetzung: Communication Arrangement

Bevor der Proxy funktioniert, benötigst du die Verbindungskonfiguration. Siehe Communication Scenarios Guide für Details.

Proxy-Aufruf implementieren

CLASS zcl_weather_service DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_weather_result,
location TYPE string,
temperature TYPE decfloat16,
humidity TYPE i,
success TYPE abap_bool,
error_msg TYPE string,
END OF ty_weather_result.
METHODS get_weather
IMPORTING iv_location TYPE string
RETURNING VALUE(rs_result) TYPE ty_weather_result.
ENDCLASS.
CLASS zcl_weather_service IMPLEMENTATION.
METHOD get_weather.
" Destination über Communication Arrangement holen
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_WEATHER_OUTBOUND'
service_id = 'Z_WEATHER_API_REST'
).
" Proxy instanziieren
DATA(lo_proxy) = NEW zscm_weather_api_proxy(
iv_destination = lo_destination
).
TRY.
" Typsicherer API-Aufruf - Eingabeparameter als Struktur
DATA(ls_request) = VALUE zs_weather_request(
location = iv_location
units = 'metric'
).
" API aufrufen - Response als generierte Struktur
DATA(ls_response) = lo_proxy->get_current_weather(
is_request = ls_request
).
" Ergebnis mappen
rs_result = VALUE #(
location = ls_response-name
temperature = ls_response-main-temp
humidity = ls_response-main-humidity
success = abap_true
).
CATCH zcx_weather_api INTO DATA(lx_api).
" Fachlicher Fehler (z.B. Location nicht gefunden)
rs_result-success = abap_false.
rs_result-error_msg = lx_api->get_text( ).
CATCH cx_http_dest_provider_error
cx_web_http_client_error INTO DATA(lx_http).
" Technischer Fehler (Netzwerk, Timeout)
rs_result-success = abap_false.
rs_result-error_msg = |Verbindungsfehler: { lx_http->get_text( ) }|.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Response-Handling und Mapping

Automatische Typen nutzen

Der große Vorteil des SCM: Du arbeitest mit ABAP-Strukturen statt mit JSON-Strings.

" OHNE SCM - manuelles JSON-Parsing
DATA(lv_json) = lo_client->execute( )->get_text( ).
/ui2/cl_json=>deserialize(
EXPORTING json = lv_json
CHANGING data = ls_weather
).
" Fehleranfällig: Feldnamen müssen exakt matchen
" MIT SCM - typsicher
DATA(ls_weather) = lo_proxy->get_current_weather( ls_request ).
" Compiler prüft Struktur, Feldnamen sind garantiert korrekt

Komplexe Responses verarbeiten

Bei verschachtelten Strukturen nutzt du die generierten Typen:

" Generierte verschachtelte Struktur
" zs_forecast_response
" ├── city: zs_city
" │ ├── name
" │ └── country
" └── list: TABLE OF zs_forecast_item
" ├── dt (timestamp)
" ├── main: zs_main_data
" │ ├── temp
" │ └── humidity
" └── weather: TABLE OF zs_weather_desc
DATA(ls_forecast) = lo_proxy->get_forecast( ls_request ).
" Zugriff auf verschachtelte Daten - vollständig typisiert
LOOP AT ls_forecast-list INTO DATA(ls_item).
DATA(lv_temp) = ls_item-main-temp.
LOOP AT ls_item-weather INTO DATA(ls_desc).
" Wetterbeschreibungen verarbeiten
DATA(lv_description) = ls_desc-description.
ENDLOOP.
ENDLOOP.

Unterschied: HTTP Client vs. SCM

SzenarioHTTP ClientService Consumption Model
API aufrufenURL + Method + Body manuellMethode mit Parametern
Request bauenJSON serialisierenABAP-Struktur befüllen
Response lesenJSON deserialisierenDirekt ABAP-Struktur
FehlertypenHTTP-Statuscodes prüfenSpezifische Exceptions
API-ÄnderungLaufzeitfehlerCompile-Fehler

Vergleich am Beispiel

" ══════════════════════════════════════════════════════════════════
" Ansatz 1: Direkter HTTP Client
" ══════════════════════════════════════════════════════════════════
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
lo_destination
).
lo_client->get_http_request( )->set_uri_path( '/weather' ).
lo_client->get_http_request( )->set_query_parameter(
name = 'q'
value = 'Berlin'
).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
DATA(lv_json) = lo_response->get_text( ).
" JSON manuell parsen - fehleranfällig
DATA: BEGIN OF ls_weather,
name TYPE string,
main TYPE REF TO data,
END OF ls_weather.
/ui2/cl_json=>deserialize(
EXPORTING json = lv_json
CHANGING data = ls_weather
).
lo_client->close( ).
" ══════════════════════════════════════════════════════════════════
" Ansatz 2: Service Consumption Model
" ══════════════════════════════════════════════════════════════════
DATA(lo_proxy) = NEW zscm_weather_api( lo_destination ).
" Typsicherer Aufruf - Compiler validiert Parameter
DATA(ls_weather) = lo_proxy->get_current_weather(
VALUE #( q = 'Berlin' )
).
" Direkter Zugriff auf typisierte Felder
DATA(lv_temp) = ls_weather-main-temp.

Fehlerbehandlung und Best Practices

Exception-Hierarchie verstehen

TRY.
DATA(ls_result) = lo_proxy->get_data( ls_request ).
CATCH zcx_scm_business_error INTO DATA(lx_business).
" Fachlicher Fehler - API hat Fehler gemeldet (z.B. 404, 422)
" Diese sind NICHT retry-fähig
CASE lx_business->http_status.
WHEN 404.
" Resource nicht gefunden
WHEN 422.
" Validierungsfehler in Request
ENDCASE.
CATCH cx_http_dest_provider_error INTO DATA(lx_dest).
" Destination-Fehler - Konfigurationsproblem
" Logging, aber kein Retry
LOG-POINT ID zlog SUBKEY 'DEST_ERROR'
FIELDS lx_dest->get_text( ).
CATCH cx_web_http_client_error INTO DATA(lx_http).
" Technischer Fehler - Netzwerk, Timeout
" Diese KÖNNEN retry-fähig sein
IF lx_http->error_type = if_web_http_client=>co_timeout.
" Retry möglich
ENDIF.
ENDTRY.

Retry-Logik implementieren

CONSTANTS: c_max_retries TYPE i VALUE 3,
c_retry_delay TYPE i VALUE 1000. " ms
DATA: lv_attempt TYPE i VALUE 1.
WHILE lv_attempt <= c_max_retries.
TRY.
DATA(ls_result) = lo_proxy->call_api( ls_request ).
EXIT. " Erfolg - Schleife verlassen
CATCH cx_web_http_client_error INTO DATA(lx_http).
IF lx_http->error_type = if_web_http_client=>co_timeout
AND lv_attempt < c_max_retries.
" Warten und erneut versuchen
cl_abap_session=>sleep( c_retry_delay * lv_attempt ).
lv_attempt = lv_attempt + 1.
ELSE.
" Kein Retry mehr möglich
RAISE EXCEPTION TYPE zcx_api_call_failed
EXPORTING
textid = zcx_api_call_failed=>api_unavailable
previous = lx_http.
ENDIF.
ENDTRY.
ENDWHILE.

Best Practices Checkliste

Best PracticeBeschreibung
Immer Exception handelnSCM-Proxys können verschiedene Exceptions werfen
Timeouts konfigurierenIm Communication Arrangement passende Werte setzen
Retry nur bei transient errorsTimeouts ja, 4xx-Fehler nein
Logging aktivierenAPI-Aufrufe protokollieren für Debugging
Credentials nie hardcodenCommunication Arrangements nutzen
API-Version beachtenBei Proxy-Regenerierung auf Breaking Changes achten

SCM in RAP integrieren

Custom Entity für externe Daten

Wenn du externe API-Daten in einer Fiori-App anzeigen willst:

" CDS Custom Entity definieren
@EndUserText.label: 'Externe Wetterdaten'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_WEATHER_QUERY'
define custom entity ZI_WeatherData
{
key Location : abap.char(100);
Temperature : abap.dec(5,2);
Humidity : abap.int4;
Description : abap.char(200);
LastUpdated : abap.timestampl;
}
" Query-Implementierung mit SCM
CLASS zcl_weather_query DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
ENDCLASS.
CLASS zcl_weather_query IMPLEMENTATION.
METHOD if_rap_query_provider~select.
" Filterparameter auslesen
DATA(lt_filters) = io_request->get_filter( )->get_as_ranges( ).
" SCM-Proxy für API-Aufruf
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_WEATHER_OUTBOUND'
).
DATA(lo_proxy) = NEW zscm_weather_api( lo_destination ).
" API aufrufen
TRY.
DATA(ls_weather) = lo_proxy->get_current_weather(
VALUE #( q = 'Berlin' )
).
" In Custom Entity-Format mappen
DATA(lt_result) = VALUE ztt_weather_data( (
location = ls_weather-name
temperature = ls_weather-main-temp
humidity = ls_weather-main-humidity
description = ls_weather-weather[ 1 ]-description
lastupdated = utclong_current( )
) ).
io_response->set_total_number_of_records( lines( lt_result ) ).
io_response->set_data( lt_result ).
CATCH cx_root INTO DATA(lx_error).
RAISE EXCEPTION TYPE zcx_rap_query_provider
EXPORTING previous = lx_error.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Für mehr Details zu externen APIs in RAP siehe RAP External API Consumption.

Häufige Fehler und Lösungen

Problem: “No metadata found”

Symptom: Proxy kann nicht erstellt werden
Ursache: EDMX/OpenAPI-Datei ist fehlerhaft oder unvollständig
Lösung:
1. API-Spezifikation im Browser validieren (Swagger Editor)
2. Fehlende Referenzen ($ref) prüfen
3. Bei OpenAPI: Version 3.0+ erforderlich

Problem: “Destination not found”

Symptom: Runtime-Fehler beim Proxy-Aufruf
Ursache: Communication Arrangement fehlt oder falsch konfiguriert
Lösung:
1. Fiori App "Communication Arrangements" öffnen
2. Scenario-ID prüfen (muss mit Code übereinstimmen)
3. Status "Active" sicherstellen
4. Service-ID im Code prüfen (service_id Parameter)

Problem: “Type mismatch in response”

Symptom: Datentyp-Fehler beim Zugriff auf Response-Felder
Ursache: API hat Schema geändert, aber Proxy wurde nicht aktualisiert
Lösung:
1. Neue API-Spezifikation herunterladen
2. SCM in ADT öffnen → Rechtsklick → "Update Service"
3. Generierte Strukturen prüfen
4. Aufrufenden Code anpassen

Weiterführende Themen

Fazit

Das Service Consumption Model ist der empfohlene Weg für die Integration stabiler externer APIs in ABAP Cloud. Die automatische Generierung von Proxy-Klassen und Datentypen spart Entwicklungszeit, erhöht die Codequalität und macht API-Änderungen sofort sichtbar. Für einfache oder dynamische Szenarien bleibt der direkte HTTP-Client eine valide Alternative.