Le SAP Destination Service est un service BTP central pour la connexion sécurisée aux systèmes externes. Il gère de manière centralisée les paramètres de connexion, les données d’authentification et les certificats, de sorte que les applications ABAP n’ont pas besoin de stocker des credentials sensibles dans le code.
Pourquoi le Destination Service ?
Le Destination Service résout plusieurs défis lors de l’intégration de systèmes externes :
| Aspect | Sans Destination Service | Avec Destination Service |
|---|---|---|
| Credentials | Dans le code ou tables de customizing | Gérés centralement dans BTP |
| Gestion des URL | Codés en dur ou SM59 | Configurables centralement |
| Authentification | À implémenter manuellement | Automatique (OAuth, Cert, etc.) |
| Changement d’environnement | Modifications de code nécessaires | Seulement changement de configuration |
| Sécurité | Risque de fuite de credentials | Secrets jamais dans le code |
| Monitoring | Pas de vue d’ensemble centrale | Dashboard BTP Cockpit |
Configuration dans le BTP Cockpit
1. Créer une destination
Naviguez dans le BTP Cockpit vers votre sous-compte et sélectionnez 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. Types d’authentification
Le Destination Service prend en charge différentes méthodes d’authentification :
| Type | Description | Cas d’usage |
|---|---|---|
NoAuthentication | Pas d’authentification | APIs publiques |
BasicAuthentication | Nom d’utilisateur/mot de passe | Systèmes legacy |
OAuth2ClientCredentials | Client ID/Secret | Machine-to-Machine |
OAuth2SAMLBearerAssertion | Échange de token SAML | Principal Propagation |
OAuth2UserTokenExchange | Transmission de token utilisateur | Conserver le contexte utilisateur |
ClientCertificateAuthentication | Certificat X.509 | APIs hautement sécurisées |
PrincipalPropagation | SAP Cloud Connector | Systèmes On-Premise |
3. Communication Arrangement dans le système ABAP
Pour utiliser les destinations depuis ABAP Cloud, vous avez besoin d’un 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 │└──────────────────────────────────────────────────────────────┘Récupérer une destination dans ABAP
Utiliser l’API Destination Service
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.Modes d’authentification
Le paramètre i_authn_mode contrôle l’obtention du token :
" Mode 1 : Authentification spécifique au service (recommandé)" Utilise les credentials configurés dans la destinationro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'WEATHER_API" i_authn_mode = if_a4c_cp_service=>service_specific ).
" Mode 2 : User Propagation" Transmet le contexte de l'utilisateur métier actuelro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'SAP_SYSTEM" i_authn_mode = if_a4c_cp_service=>user_propagation ).
" Mode 3 : Named User" Utilise un utilisateur technique fixe de la destinationro_destination = cl_http_destination_provider=>create_by_cloud_destination( i_name = 'LEGACY_SYSTEM" i_authn_mode = if_a4c_cp_service=>named_user ).Client HTTP avec Destination
Requête GET simple
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.Requête POST avec corps JSON
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.Authentification OAuth2
Flux OAuth2 Client Credentials
L’authentification Machine-to-Machine la plus courante :
┌─────────────┐ ┌──────────────────┐ ┌──────────────┐│ 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 │└─────────────┘ └──────────────────┘ └──────────────┘Configuration de destination pour 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 avec échange de token utilisateur
Pour les scénarios où le contexte de l’utilisateur métier doit être transmis :
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.Exemple complet : Intégration d’API REST
Classe wrapper d’API
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.Configuration de destination (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-IntegrationPour un accès authentifié (Personal Access Token) :
Name: GITHUB_APIType: HTTPURL: https://api.github.comProxy Type: InternetAuthentication: BasicAuthenticationUser: <github-username>Password: <personal-access-token>Gestion des erreurs
Gestion robuste des erreurs
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.Logique de nouvelle tentative
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.Bonnes pratiques
| Sujet | Recommandation |
|---|---|
| Noms de destination | Noms parlants (ex. CRM_PRODUCTION, WEATHER_DEV) |
| Environnements | Destinations séparées pour DEV/QA/PROD |
| Gestion des erreurs | Toujours gérer cx_http_dest_provider_error et cx_web_http_client_error |
| Pool de connexions | Libérer le client HTTP après utilisation avec close() |
| Timeouts | Configurer des timeouts appropriés dans la destination |
| Logging | Logger les appels API pour le débogage (sans credentials) |
| Mise en cache des tokens | Le Destination Service met automatiquement en cache les tokens |
| Monitoring | Utiliser SAP Cloud ALM pour le monitoring des API |
Sujets avancés
- SAP Event Mesh avec ABAP Cloud - Intégration basée sur les événements
- RAP avec Custom Entities - Intégrer des données externes dans RAP
- SAP Build Work Zone Integration - Fournir des apps dans le Launchpad