Der SAP Destination Service ist ein zentraler BTP-Service für die sichere Verbindung zu externen Systemen. Er verwaltet Verbindungsparameter, Authentifizierungsdaten und Zertifikate zentral, sodass ABAP-Anwendungen keine sensiblen Credentials im Code speichern müssen.
Warum Destination Service?
Der Destination Service löst mehrere Herausforderungen bei der Integration externer Systeme:
| Aspekt | Ohne Destination Service | Mit Destination Service |
|---|---|---|
| Credentials | Im Code oder Customizing-Tabellen | Zentral in BTP verwaltet |
| URL-Verwaltung | Hardcoded oder SM59 | Zentral konfigurierbar |
| Authentifizierung | Manuell implementieren | Automatisch (OAuth, Cert, etc.) |
| Umgebungswechsel | Code-Änderungen nötig | Nur Konfigurationsänderung |
| Sicherheit | Risiko bei Credential-Leaks | Secrets nie im Code |
| Monitoring | Keine zentrale Übersicht | BTP Cockpit Dashboard |
Konfiguration im BTP Cockpit
1. Destination erstellen
Navigiere im BTP Cockpit zu deinem Subaccount und wähle Connectivity → Destinations:
┌──────────────────────────────────────────────────────────────┐│ Destination Configuration │├──────────────────────────────────────────────────────────────┤│ Name: WEATHER_API ││ Type: HTTP ││ URL: https://api.weatherservice.com/v1 ││ Proxy Type: Internet ││ Authentication: OAuth2ClientCredentials ││ ││ ┌─ OAuth2 Settings ───────────────────────────────────────┐ ││ │ Client ID: abc123-client-id │ ││ │ Client Secret: ******** │ ││ │ Token URL: https://auth.weatherservice.com/token │ ││ └─────────────────────────────────────────────────────────┘ ││ ││ Additional Properties: ││ ├── HTML5.DynamicDestination: true ││ └── WebIDEEnabled: true │└──────────────────────────────────────────────────────────────┘2. Authentifizierungstypen
Der Destination Service unterstützt verschiedene Authentifizierungsmethoden:
| Typ | Beschreibung | Use Case |
|---|---|---|
NoAuthentication | Keine Authentifizierung | Öffentliche APIs |
BasicAuthentication | Benutzername/Passwort | Legacy-Systeme |
OAuth2ClientCredentials | Client ID/Secret | Machine-to-Machine |
OAuth2SAMLBearerAssertion | SAML Token Exchange | Principal Propagation |
OAuth2UserTokenExchange | User Token Weitergabe | User-Kontext erhalten |
ClientCertificateAuthentication | X.509 Zertifikat | Hochsichere APIs |
PrincipalPropagation | SAP Cloud Connector | On-Premise Systeme |
3. Communication Arrangement im ABAP-System
Um Destinations aus ABAP Cloud zu nutzen, benötigst du ein Communication Arrangement:
┌──────────────────────────────────────────────────────────────┐│ Communication Arrangement │├──────────────────────────────────────────────────────────────┤│ Scenario: SAP_COM_0276 (Destination Service Integration)││ ││ Communication System: BTP_DESTINATION_SERVICE ││ ├── Host: destination-configuration.cfapps.eu10.hana... ││ ├── Port: 443 ││ └── Auth: OAuth 2.0 (Service Binding) ││ ││ Inbound Services: - ││ Outbound Services: ││ └── Destination Service API: Active │└──────────────────────────────────────────────────────────────┘Destination in ABAP abrufen
Destination Service API nutzen
CLASS zcl_destination_demo DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
PRIVATE SECTION. METHODS get_destination RETURNING VALUE(ro_destination) TYPE REF TO if_http_destination RAISING cx_http_dest_provider_error.ENDCLASS.
CLASS zcl_destination_demo IMPLEMENTATION. METHOD if_oo_adt_classrun~main. TRY. DATA(lo_destination) = get_destination( ). out->write( |Destination erfolgreich abgerufen| ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest). out->write( |Fehler: { lx_dest->get_text( ) }| ). ENDTRY. ENDMETHOD.
METHOD get_destination. " Destination vom BTP Destination Service abrufen ro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'WEATHER_API' i_service_instance_name = 'destination-service-instance' i_authn_mode = if_a4c_cp_service=>service_specific ). ENDMETHOD.ENDCLASS.Authentifizierungsmodi
Der Parameter i_authn_mode steuert die Token-Beschaffung:
" Modus 1: Service-spezifische Authentifizierung (empfohlen)" Verwendet die in der Destination konfigurierten Credentialsro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'WEATHER_API' i_authn_mode = if_a4c_cp_service=>service_specific ).
" Modus 2: User Propagation" Gibt den aktuellen Business-User-Kontext weiterro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'SAP_SYSTEM' i_authn_mode = if_a4c_cp_service=>user_propagation ).
" Modus 3: Named User" Verwendet festen technischen Benutzer aus Destinationro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'LEGACY_SYSTEM' i_authn_mode = if_a4c_cp_service=>named_user ).HTTP Client mit Destination
Einfacher GET-Request
CLASS zcl_weather_client DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_weather, city TYPE string, temperature TYPE decfloat16, humidity TYPE i, description TYPE string, END OF ty_weather.
METHODS get_weather IMPORTING iv_city TYPE string RETURNING VALUE(rs_result) TYPE ty_weather RAISING cx_http_dest_provider_error cx_web_http_client_error.ENDCLASS.
CLASS zcl_weather_client IMPLEMENTATION. METHOD get_weather. " 1. Destination abrufen DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'WEATHER_API' i_authn_mode = if_a4c_cp_service=>service_specific ).
" 2. HTTP Client erstellen DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
" 3. Request konfigurieren DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( |/weather?city={ iv_city }| ).
" 4. Request ausführen DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
" 5. Response verarbeiten DATA(lv_status) = lo_response->get_status( )-code. IF lv_status = 200. DATA(lv_json) = lo_response->get_text( ).
" JSON parsen /ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = rs_result ). ELSE. RAISE EXCEPTION TYPE cx_web_http_client_error. ENDIF.
" 6. Client schließen lo_client->close( ). ENDMETHOD.ENDCLASS.POST-Request mit JSON-Body
CLASS zcl_order_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_order_request, customer_id TYPE string, product_id TYPE string, quantity TYPE i, END OF ty_order_request,
BEGIN OF ty_order_response, order_id TYPE string, status TYPE string, total_amount TYPE decfloat16, END OF ty_order_response.
METHODS create_order IMPORTING is_order TYPE ty_order_request RETURNING VALUE(rs_result) TYPE ty_order_response RAISING cx_http_dest_provider_error cx_web_http_client_error.ENDCLASS.
CLASS zcl_order_service IMPLEMENTATION. METHOD create_order. " Destination und Client DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'ORDER_SERVICE_API' i_authn_mode = if_a4c_cp_service=>service_specific ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
" Request konfigurieren DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( '/orders' ).
" JSON-Body erstellen DATA(lv_json) = /ui2/cl_json=>serialize( data = is_order compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
lo_request->set_text( lv_json ). lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/json' ).
" POST ausführen DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
" Response verarbeiten IF lo_response->get_status( )-code = 201. /ui2/cl_json=>deserialize( EXPORTING json = lo_response->get_text( ) CHANGING data = rs_result ). ENDIF.
lo_client->close( ). ENDMETHOD.ENDCLASS.OAuth2-Authentifizierung
OAuth2 Client Credentials Flow
Die häufigste Machine-to-Machine-Authentifizierung:
┌─────────────┐ ┌──────────────────┐ ┌──────────────┐│ ABAP Cloud │ │ Destination Svc │ │ OAuth Server ││ │ │ │ │ ││ Request │────>│ 1. Get Dest │ │ ││ Destination│ │ │ │ ││ │<────│ 2. Dest Config │ │ ││ │ │ │ │ ││ HTTP Call │────>│ 3. Get Token │────>│ ││ │ │ (Client Creds) │ │ ││ │<────│ 4. Access Token │<────│ ││ │ │ │ │ ││ API Call │─────────────────────────────>│ 5. Validate ││ + Token │ │ │ │ Token ││ │<─────────────────────────────│ 6. Response │└─────────────┘ └──────────────────┘ └──────────────┘Destination-Konfiguration für OAuth2
Name: EXTERNAL_CRM_APIType: HTTPURL: https://api.crm-system.com/v2Proxy Type: InternetAuthentication: OAuth2ClientCredentials
Token Service URL: https://auth.crm-system.com/oauth/tokenClient ID: crm-integration-clientClient Secret: ********
Additional Properties: tokenServiceURLType: Dedicated scope: api.read api.writeOAuth2 mit User Token Exchange
Für Szenarien, in denen der Business-User-Kontext weitergegeben werden soll:
METHOD call_with_user_context. " Destination mit User Token Exchange DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'BACKEND_SYSTEM' i_authn_mode = if_a4c_cp_service=>user_propagation ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
" Der HTTP-Request enthält automatisch den User-Token " Backend-System kann den Benutzer identifizieren DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
lo_client->close( ).ENDMETHOD.Vollständiges Beispiel: REST API Integration
API-Wrapper-Klasse
CLASS zcl_github_api DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_repository, id TYPE i, name TYPE string, full_name TYPE string, description TYPE string, html_url TYPE string, stargazers_count TYPE i, END OF ty_repository, tt_repositories TYPE STANDARD TABLE OF ty_repository WITH EMPTY KEY.
METHODS constructor RAISING cx_http_dest_provider_error.
METHODS get_user_repos IMPORTING iv_username TYPE string RETURNING VALUE(rt_result) TYPE tt_repositories RAISING cx_web_http_client_error.
METHODS create_issue IMPORTING iv_owner TYPE string iv_repo TYPE string iv_title TYPE string iv_body TYPE string RETURNING VALUE(rv_url) TYPE string RAISING cx_web_http_client_error.
PRIVATE SECTION. DATA mo_destination TYPE REF TO if_http_destination.
METHODS execute_request IMPORTING iv_path TYPE string iv_method TYPE i DEFAULT if_web_http_client=>get iv_body TYPE string OPTIONAL RETURNING VALUE(rv_result) TYPE string RAISING cx_web_http_client_error.ENDCLASS.
CLASS zcl_github_api IMPLEMENTATION. METHOD constructor. " Destination einmalig abrufen mo_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'GITHUB_API' i_authn_mode = if_a4c_cp_service=>service_specific ). ENDMETHOD.
METHOD get_user_repos. DATA(lv_json) = execute_request( iv_path = |/users/{ iv_username }/repos| ).
/ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = rt_result ). ENDMETHOD.
METHOD create_issue. DATA: BEGIN OF ls_issue, title TYPE string, body TYPE string, END OF ls_issue.
ls_issue-title = iv_title. ls_issue-body = iv_body.
DATA(lv_body) = /ui2/cl_json=>serialize( data = ls_issue compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-low_case ).
DATA(lv_json) = execute_request( iv_path = |/repos/{ iv_owner }/{ iv_repo }/issues| iv_method = if_web_http_client=>post iv_body = lv_body ).
" URL aus Response extrahieren DATA: BEGIN OF ls_response, html_url TYPE string, END OF ls_response.
/ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_response ).
rv_url = ls_response-html_url. ENDMETHOD.
METHOD execute_request. DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = mo_destination ).
DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( iv_path ).
IF iv_body IS NOT INITIAL. lo_request->set_text( iv_body ). lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/json' ). ENDIF.
lo_request->set_header_field( i_name = 'Accept' i_value = 'application/vnd.github.v3+json' ).
DATA(lo_response) = lo_client->execute( iv_method ). DATA(lv_status) = lo_response->get_status( )-code.
IF lv_status >= 200 AND lv_status < 300. rv_result = lo_response->get_text( ). ELSE. DATA(lv_error) = lo_response->get_text( ). lo_client->close( ). RAISE EXCEPTION TYPE cx_web_http_client_error EXPORTING textid = cx_web_http_client_error=>http_error. ENDIF.
lo_client->close( ). ENDMETHOD.ENDCLASS.Destination-Konfiguration (GitHub)
Name: GITHUB_APIType: HTTPURL: https://api.github.comProxy Type: InternetAuthentication: NoAuthentication (oder OAuth2ClientCredentials für höhere Rate Limits)
Additional Properties: User-Agent: ABAP-Cloud-IntegrationFür authentifizierten Zugriff (Personal Access Token):
Name: GITHUB_APIType: HTTPURL: https://api.github.comProxy Type: InternetAuthentication: BasicAuthenticationUser: <github-username>Password: <personal-access-token>Error Handling
Robuste Fehlerbehandlung
CLASS zcl_api_client DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. CLASS-METHODS call_api IMPORTING iv_destination TYPE string iv_path TYPE string RETURNING VALUE(rv_result) TYPE string RAISING zcx_api_error.ENDCLASS.
CLASS zcl_api_client IMPLEMENTATION. METHOD call_api. DATA lo_client TYPE REF TO if_web_http_client.
TRY. " Destination abrufen DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination( i_name = iv_destination i_authn_mode = if_a4c_cp_service=>service_specific ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest). " Destination nicht gefunden oder Konfigurationsfehler RAISE EXCEPTION TYPE zcx_api_error EXPORTING previous = lx_dest textid = zcx_api_error=>destination_error. ENDTRY.
TRY. " Client erstellen und Request ausführen lo_client = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( iv_path ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ). DATA(lv_status) = lo_response->get_status( )-code.
CASE lv_status. WHEN 200 OR 201. rv_result = lo_response->get_text( ).
WHEN 401. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>authentication_failed.
WHEN 403. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>authorization_failed.
WHEN 404. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>not_found.
WHEN 429. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>rate_limit_exceeded.
WHEN 500 OR 502 OR 503 OR 504. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>server_error.
WHEN OTHERS. RAISE EXCEPTION TYPE zcx_api_error EXPORTING textid = zcx_api_error=>unexpected_error. ENDCASE.
CATCH cx_web_http_client_error INTO DATA(lx_http). " Netzwerkfehler, Timeout, etc. RAISE EXCEPTION TYPE zcx_api_error EXPORTING previous = lx_http textid = zcx_api_error=>connection_error. ENDTRY.
IF lo_client IS BOUND. lo_client->close( ). ENDIF. ENDMETHOD.ENDCLASS.Retry-Logik
METHOD call_with_retry. CONSTANTS: lc_max_retries TYPE i VALUE 3, lc_retry_delay TYPE i VALUE 1000. " Millisekunden
DATA lv_retries TYPE i.
WHILE lv_retries < lc_max_retries. TRY. rv_result = call_api( iv_destination = iv_destination iv_path = iv_path ). RETURN. " Erfolg
CATCH zcx_api_error INTO DATA(lx_error). " Nur bei temporären Fehlern retry IF lx_error->is_retryable( ). lv_retries = lv_retries + 1. IF lv_retries < lc_max_retries. " Warten vor erneutem Versuch cl_abap_session=>sleep( lc_retry_delay * lv_retries ). ELSE. RAISE EXCEPTION lx_error. ENDIF. ELSE. RAISE EXCEPTION lx_error. ENDIF. ENDTRY. ENDWHILE.ENDMETHOD.Best Practices
| Thema | Empfehlung |
|---|---|
| Destination-Namen | Sprechende Namen (z.B. CRM_PRODUCTION, WEATHER_DEV) |
| Environments | Separate Destinations für DEV/QA/PROD |
| Error Handling | Immer cx_http_dest_provider_error und cx_web_http_client_error behandeln |
| Connection Pool | HTTP-Client nach Nutzung mit close() freigeben |
| Timeouts | Angemessene Timeouts in Destination konfigurieren |
| Logging | API-Aufrufe für Debugging loggen (ohne Credentials) |
| Token Caching | Destination Service cached Tokens automatisch |
| Monitoring | SAP Cloud ALM für API-Monitoring nutzen |
Weiterführende Themen
- SAP Event Mesh mit ABAP Cloud - Event-basierte Integration
- RAP mit Custom Entities - Externe Daten in RAP einbinden
- SAP Build Work Zone Integration - Apps im Launchpad bereitstellen