Service Consumption Model - Consommer des APIs externes de manière typée

Catégorie
Integration
Publié
Auteur
Johannes

Le Service Consumption Model (SCM) est la méthode recommandée pour consommer des APIs REST et OData externes de manière typée dans ABAP Cloud. Contrairement à l’appel direct du client HTTP, le SCM génère automatiquement des classes proxy ABAP et des types de données à partir de la spécification API - cela fait gagner du temps de développement et évite les erreurs.

Aperçu : SCM vs. Client HTTP

AspectService Consumption ModelClient HTTP direct
TypageComplet - types générésAucun - traitement JSON manuel
EffortUne fois : Import + ConfigurationChaque appel : Construire la requête, parser la réponse
MaintenabilitéÉlevée - changements API visiblesFaible - erreurs uniquement à l’exécution
FlexibilitéLimitée aux APIs définiesFlexibilité maximale
Cas d’usageAPIs externes stables (OData, OpenAPI)Appels ad-hoc, APIs dynamiques

Quand choisir quelle approche ?

Utiliser le Service Consumption Model quand :

  • Vous consommez une API OData ou REST stable
  • L’API a une spécification EDMX ou OpenAPI
  • Le typage et la maintenabilité sont importants
  • Plusieurs opérations API sont utilisées

Utiliser le Client HTTP direct quand :

  • L’API n’a pas de spécification formelle
  • Vous n’avez besoin que de quelques appels simples
  • L’API change fréquemment
  • Vous avez besoin d’un contrôle maximal sur Request/Response

Pour les détails sur l’approche client HTTP directe, voir ABAP HTTP Client : Appeler des APIs REST.

Formats API supportés

Le Service Consumption Model supporte trois formats :

┌─────────────────────────────────────────────────────────────────────────┐
│ Service Consumption Model - Formats supportés │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ OData V2 (EDMX) │ │ OData V4 (EDMX) │ │ OpenAPI 3.0 │ │
│ │ │ │ │ │ (JSON/YAML) │ │
│ │ - SAP Standard │ │ - OData moderne │ │ - APIs REST │ │
│ │ - APIs S/4HANA │ │ - Services RAP │ │ - APIs externes│ │
│ │ - SuccessFactors │ │ - Services BTP │ │ - APIs Cloud │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
│ │
│ Import dans ADT : │
│ File → New → Other ABAP Repository Object │
│ → Connectivity → Service Consumption Model │
└─────────────────────────────────────────────────────────────────────────┘

Étape par étape : Créer un SCM

1. Obtenir la spécification API

D’abord, vous avez besoin du fichier EDMX ou OpenAPI de l’API cible :

APIs OData (EDMX) :

# SAP API Business Hub
https://api.sap.com → Sélectionner API → API Specification → Download EDMX
# Depuis un système SAP en cours d'exécution
GET https://<host>/sap/opu/odata/sap/<SERVICE_NAME>/$metadata

APIs REST (OpenAPI) :

# APIs publiques
https://api.example.com/openapi.json
https://api.example.com/swagger.json
# Services SAP BTP
https://api.sap.com → Sélectionner Service → Download OpenAPI spec

2. Créer le Service Consumption Model

Dans ADT (ABAP Development Tools) :

  1. Clic droit sur Package → NewOther ABAP Repository Object
  2. Catégorie : ConnectivityService Consumption Model
  3. Remplir l’assistant :
ChampExempleDescription
NameZSCM_WEATHER_APINom technique
DescriptionWeather API ConsumerBrève description
Remote Consumption ModeOData ou Web ServiceType d’API
  1. Importer le fichier de métadonnées (EDMX, OpenAPI JSON, ou WSDL)
  2. Finish - ADT génère tous les artefacts

3. Comprendre les artefacts générés

Après l’import, vous trouverez les objets suivants :

ZSCM_WEATHER_API (Service Consumption Model)
├── ZSCM_WEATHER_API_PROXY (Classe Proxy)
│ ├── get_weather_forecast() " Opération de l'API
│ ├── get_current_weather() " Opération de l'API
│ └── create_alert() " Opération de l'API
├── ZS_WEATHER_FORECAST (Structure)
│ ├── location TYPE string
│ ├── temp_min TYPE decfloat16
│ └── temp_max TYPE decfloat16
├── ZS_CURRENT_WEATHER (Structure)
│ └── ...
└── ZCX_WEATHER_API (Classe Exception)
└── Erreurs métier/techniques

Exemple de code : Appel API avec proxy SCM

Prérequis : Communication Arrangement

Avant que le proxy ne fonctionne, vous avez besoin de la configuration de connexion. Voir Communication Scenarios Guide pour les détails.

Implémenter l’appel proxy

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.
" Obtenir la destination via Communication Arrangement
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_WEATHER_OUTBOUND"
service_id = 'Z_WEATHER_API_REST"
).
" Instancier le proxy
DATA(lo_proxy) = NEW zscm_weather_api_proxy(
iv_destination = lo_destination
).
TRY.
" Appel API typé - paramètres d'entrée comme structure
DATA(ls_request) = VALUE zs_weather_request(
location = iv_location
units = 'metric"
).
" Appeler l'API - Response comme structure générée
DATA(ls_response) = lo_proxy->get_current_weather(
is_request = ls_request
).
" Mapper le résultat
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).
" Erreur métier (ex. Location non trouvée)
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).
" Erreur technique (réseau, timeout)
rs_result-success = abap_false.
rs_result-error_msg = |Erreur de connexion : { lx_http->get_text( ) }|.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Gestion des réponses et mapping

Utiliser les types automatiques

Le grand avantage du SCM : Vous travaillez avec des structures ABAP au lieu de chaînes JSON.

" SANS SCM - parsing JSON manuel
DATA(lv_json) = lo_client->execute( )->get_text( ).
/ui2/cl_json=>deserialize(
EXPORTING json = lv_json
CHANGING data = ls_weather
).
" Sujet aux erreurs : les noms de champs doivent correspondre exactement
" AVEC SCM - typé
DATA(ls_weather) = lo_proxy->get_current_weather( ls_request ).
" Le compilateur vérifie la structure, les noms de champs sont garantis corrects

Traiter les réponses complexes

Pour les structures imbriquées, utilisez les types générés :

" Structure imbriquée générée
" 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 ).
" Accès aux données imbriquées - entièrement typé
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).
" Traiter les descriptions météo
DATA(lv_description) = ls_desc-description.
ENDLOOP.
ENDLOOP.

Différence : Client HTTP vs. SCM

ScénarioClient HTTPService Consumption Model
Appeler l’APIURL + Method + Body manuellementMéthode avec paramètres
Construire la requêteSérialiser JSONRemplir structure ABAP
Lire la réponseDésérialiser JSONStructure ABAP directe
Types d’erreursVérifier codes statut HTTPExceptions spécifiques
Changement APIErreur à l’exécutionErreur de compilation

Comparaison par l’exemple

" ══════════════════════════════════════════════════════════════════
" Approche 1 : Client HTTP direct
" ══════════════════════════════════════════════════════════════════
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( ).
" Parser JSON manuellement - sujet aux erreurs
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( ).
" ══════════════════════════════════════════════════════════════════
" Approche 2 : Service Consumption Model
" ══════════════════════════════════════════════════════════════════
DATA(lo_proxy) = NEW zscm_weather_api( lo_destination ).
" Appel typé - le compilateur valide les paramètres
DATA(ls_weather) = lo_proxy->get_current_weather(
VALUE #( q = 'Berlin' )
).
" Accès direct aux champs typés
DATA(lv_temp) = ls_weather-main-temp.

Gestion des erreurs et bonnes pratiques

Comprendre la hiérarchie des exceptions

TRY.
DATA(ls_result) = lo_proxy->get_data( ls_request ).
CATCH zcx_scm_business_error INTO DATA(lx_business).
" Erreur métier - l'API a signalé une erreur (ex. 404, 422)
" Celles-ci ne sont PAS réessayables
CASE lx_business->http_status.
WHEN 404.
" Ressource non trouvée
WHEN 422.
" Erreur de validation dans la requête
ENDCASE.
CATCH cx_http_dest_provider_error INTO DATA(lx_dest).
" Erreur de destination - problème de configuration
" Logging, mais pas de retry
LOG-POINT ID zlog SUBKEY 'DEST_ERROR"
FIELDS lx_dest->get_text( ).
CATCH cx_web_http_client_error INTO DATA(lx_http).
" Erreur technique - réseau, timeout
" Celles-ci PEUVENT être réessayables
IF lx_http->error_type = if_web_http_client=>co_timeout.
" Retry possible
ENDIF.
ENDTRY.

Implémenter une logique de retry

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. " Succès - sortir de la boucle
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.
" Attendre et réessayer
cl_abap_session=>sleep( c_retry_delay * lv_attempt ).
lv_attempt = lv_attempt + 1.
ELSE.
" Plus de retry possible
RAISE EXCEPTION TYPE zcx_api_call_failed
EXPORTING
textid = zcx_api_call_failed=>api_unavailable
previous = lx_http.
ENDIF.
ENDTRY.
ENDWHILE.

Checklist des bonnes pratiques

Bonne pratiqueDescription
Toujours gérer les exceptionsLes proxys SCM peuvent lever diverses exceptions
Configurer les timeoutsDéfinir des valeurs appropriées dans le Communication Arrangement
Retry uniquement pour erreurs transitoiresTimeouts oui, erreurs 4xx non
Activer le loggingJournaliser les appels API pour le débogage
Ne jamais hardcoder les credentialsUtiliser les Communication Arrangements
Surveiller la version APIAttention aux breaking changes lors de la régénération du proxy

Intégrer le SCM dans RAP

Custom Entity pour données externes

Si vous voulez afficher des données d’API externe dans une app Fiori :

" Définir CDS Custom Entity
@EndUserText.label: 'Données météo externes"
@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;
}
" Implémentation Query avec 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.
" Lire les paramètres de filtre
DATA(lt_filters) = io_request->get_filter( )->get_as_ranges( ).
" Proxy SCM pour appel API
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 ).
" Appeler l'API
TRY.
DATA(ls_weather) = lo_proxy->get_current_weather(
VALUE #( q = 'Berlin' )
).
" Mapper au format Custom Entity
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.

Pour plus de détails sur les APIs externes dans RAP, voir RAP External API Consumption.

Erreurs courantes et solutions

Problème : “No metadata found”

Symptôme : Impossible de créer le proxy
Cause : Le fichier EDMX/OpenAPI est défectueux ou incomplet
Solution :
1. Valider la spécification API dans le navigateur (Swagger Editor)
2. Vérifier les références manquantes ($ref)
3. Pour OpenAPI : Version 3.0+ requise

Problème : “Destination not found”

Symptôme : Erreur runtime lors de l'appel proxy
Cause : Communication Arrangement manquant ou mal configuré
Solution :
1. Ouvrir l'app Fiori "Communication Arrangements"
2. Vérifier le Scenario-ID (doit correspondre au code)
3. S'assurer du statut "Active"
4. Vérifier le service_id dans le code (paramètre service_id)

Problème : “Type mismatch in response”

Symptôme : Erreurs de type de données lors de l'accès aux champs Response
Cause : L'API a changé le schéma, mais le proxy n'a pas été mis à jour
Solution :
1. Télécharger la nouvelle spécification API
2. Ouvrir SCM dans ADT → Clic droit → "Update Service"
3. Vérifier les structures générées
4. Adapter le code appelant

Sujets connexes

Conclusion

Le Service Consumption Model est la méthode recommandée pour l’intégration d’APIs externes stables dans ABAP Cloud. La génération automatique de classes proxy et de types de données fait gagner du temps de développement, améliore la qualité du code et rend les changements d’API immédiatement visibles. Pour les scénarios simples ou dynamiques, le client HTTP direct reste une alternative valide.