OData (Open Data Protocol) est le standard pour les APIs RESTful dans SAP. Avec SAP Gateway et le ABAP RESTful Application Programming Model (RAP), des services web modernes peuvent etre crees.
Fondamentaux OData
| Concept | Description |
|---|---|
| Entity | Objet de donnees (par ex. Commande) |
| Entity Set | Collection d’Entities |
| Navigation Property | Relation entre Entities |
| Service Document | Decrit les ressources disponibles |
Methodes HTTP et CRUD
| Methode | Operation | Methode DPC |
|---|---|---|
| GET | Read | GET_ENTITY, GET_ENTITYSET |
| POST | Create | CREATE_ENTITY |
| PUT | Update (complet) | UPDATE_ENTITY |
| PATCH | Update (partiel) | UPDATE_ENTITY |
| DELETE | Delete | DELETE_ENTITY |
Gateway classique (SEGW)
Data Provider Class - Lire une entity
METHOD /iwbep/if_mgw_appl_srv_runtime~get_entity.
DATA: ls_key TYPE /iwbep/s_mgw_name_value_pair, ls_order TYPE zcl_zorders_mpc=>ts_order.
" Lire la cle depuis la requete READ TABLE it_key_tab INTO ls_key WITH KEY name = 'OrderId'. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING textid = /iwbep/cx_mgw_busi_exception=>business_error message = 'OrderId est requis'. ENDIF.
" Lire les donnees depuis la base de donnees SELECT SINGLE * FROM zorders INTO CORRESPONDING FIELDS OF ls_order WHERE order_id = ls_key-value.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING textid = /iwbep/cx_mgw_busi_exception=>business_error_with_nav message = 'Commande non trouvee'. ENDIF.
" Retourner le resultat er_entity = ls_order.
ENDMETHOD.Entity Set avec filtre et pagination
METHOD /iwbep/if_mgw_appl_srv_runtime~get_entityset.
DATA: lt_orders TYPE zcl_zorders_mpc=>tt_order, lt_filter TYPE /iwbep/t_mgw_select_option, ls_paging TYPE /iwbep/s_mgw_paging.
" Lire les options de filtre lt_filter = io_tech_request_context->get_filter( )->get_filter_select_options( ).
" Parametres de pagination ls_paging = io_tech_request_context->get_paging( ).
" Creer la condition WHERE dynamique DATA(lo_filter) = io_tech_request_context->get_filter( ). DATA(lv_where) = lo_filter->get_filter_string( ).
" Lire les donnees SELECT * FROM zorders WHERE (lv_where) ORDER BY order_id INTO CORRESPONDING FIELDS OF TABLE lt_orders UP TO ls_paging-top ROWS OFFSET ls_paging-skip.
" Nombre total pour $inlinecount IF io_tech_request_context->has_inlinecount( ) = abap_true. SELECT COUNT(*) FROM zorders WHERE (lv_where) INTO @DATA(lv_count). es_response_context-inlinecount = lv_count. ENDIF.
et_entityset = lt_orders.
ENDMETHOD.Creer une entity
METHOD /iwbep/if_mgw_appl_srv_runtime~create_entity.
DATA: ls_order_in TYPE zcl_zorders_mpc=>ts_order, ls_order_out TYPE zcl_zorders_mpc=>ts_order.
" Lire le corps de la requete io_data_provider->read_entry_data( IMPORTING es_data = ls_order_in ).
" Validation IF ls_order_in-customer_id IS INITIAL. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING message = 'Customer ID est requis'. ENDIF.
" Generer un nouvel ID SELECT MAX( order_id ) FROM zorders INTO @DATA(lv_max_id). ls_order_in-order_id = lv_max_id + 1. ls_order_in-created_at = sy-datum. ls_order_in-created_by = sy-uname.
" Enregistrer les donnees INSERT zorders FROM ls_order_in.
IF sy-subrc = 0. ls_order_out = ls_order_in. er_entity = ls_order_out. ELSE. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING message = 'Erreur lors de la creation'. ENDIF.
ENDMETHOD.Mettre a jour une entity
METHOD /iwbep/if_mgw_appl_srv_runtime~update_entity.
DATA: ls_order TYPE zcl_zorders_mpc=>ts_order, ls_key TYPE /iwbep/s_mgw_name_value_pair.
" Lire la cle et les donnees READ TABLE it_key_tab INTO ls_key WITH KEY name = 'OrderId'. io_data_provider->read_entry_data( IMPORTING es_data = ls_order ).
" Verifier la commande SELECT SINGLE * FROM zorders INTO @DATA(ls_existing) WHERE order_id = @ls_key-value.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING message = 'Commande non trouvee'. ENDIF.
" Effectuer la mise a jour ls_order-order_id = ls_key-value. ls_order-changed_at = sy-datum. ls_order-changed_by = sy-uname.
UPDATE zorders FROM ls_order.
er_entity = ls_order.
ENDMETHOD.Supprimer une entity
METHOD /iwbep/if_mgw_appl_srv_runtime~delete_entity.
DATA: ls_key TYPE /iwbep/s_mgw_name_value_pair.
READ TABLE it_key_tab INTO ls_key WITH KEY name = 'OrderId'.
DELETE FROM zorders WHERE order_id = ls_key-value.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING message = 'Suppression echouee'. ENDIF.
ENDMETHOD.Navigation Property
METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset.
DATA: lt_orders TYPE zcl_zorders_mpc=>tt_order, lt_items TYPE zcl_zorders_mpc=>tt_orderitem.
" Charger les commandes avec $expand=Items SELECT * FROM zorders INTO CORRESPONDING FIELDS OF TABLE lt_orders.
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<ls_order>). " Charger les positions pour la commande SELECT * FROM zorderitems WHERE order_id = <ls_order>-order_id INTO CORRESPONDING FIELDS OF TABLE lt_items.
" Ajouter les donnees expand APPEND VALUE #( key = <ls_order>-order_id data = lt_items ) TO et_expanded_tech_clauses. ENDLOOP.
et_entityset = lt_orders.
ENDMETHOD.Deep Insert
METHOD /iwbep/if_mgw_appl_srv_runtime~create_deep_entity.
DATA: ls_order TYPE zcl_zorders_mpc=>ts_order_deep.
" Lire la structure deep io_data_provider->read_entry_data( IMPORTING es_data = ls_order ).
" Creer la commande INSERT zorders FROM CORRESPONDING #( ls_order ).
" Creer les positions LOOP AT ls_order-items ASSIGNING FIELD-SYMBOL(<ls_item>). <ls_item>-order_id = ls_order-order_id. INSERT zorderitems FROM CORRESPONDING #( <ls_item> ). ENDLOOP.
er_deep_entity = ls_order.
ENDMETHOD.Function Import
METHOD /iwbep/if_mgw_appl_srv_runtime~execute_action.
CASE iv_action_name. WHEN 'ConfirmOrder'. " Lire les parametres DATA(lv_order_id) = VALUE #( it_parameter[ name = 'OrderId' ]-value OPTIONAL ).
" Confirmer la commande UPDATE zorders SET status = 'C' WHERE order_id = lv_order_id.
IF sy-subrc = 0. er_data = VALUE zcl_zorders_mpc=>ts_result( success = abap_true ). ENDIF.
WHEN 'GetOrderStatistics'. SELECT COUNT(*) AS total, SUM( CASE WHEN status = 'O' THEN 1 ELSE 0 END ) AS open, SUM( CASE WHEN status = 'C' THEN 1 ELSE 0 END ) AS closed FROM zorders INTO @DATA(ls_stats).
er_data = ls_stats. ENDCASE.
ENDMETHOD.Services OData bases sur RAP (Moderne)
CDS View avec Service Definition
@AbapCatalog.viewEnhancementCategory: [#NONE]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Commandes"@Metadata.allowExtensions: truedefine root view entity ZI_Order as select from zorders composition [0..*] of ZI_OrderItem as _Items{ key order_id as OrderId, customer_id as CustomerId, order_date as OrderDate, status as Status, total_amount as TotalAmount, currency as Currency,
@Semantics.user.createdBy: true created_by as CreatedBy, @Semantics.systemDateTime.createdAt: true created_at as CreatedAt,
_Items}Projection View
@EndUserText.label: 'Commandes Projection"@AccessControl.authorizationCheck: #CHECK@Metadata.allowExtensions: truedefine root view entity ZC_Order provider contract transactional_query as projection on ZI_Order{ key OrderId, CustomerId, OrderDate, Status, @Semantics.amount.currencyCode: 'Currency" TotalAmount, Currency,
_Items : redirected to composition child ZC_OrderItem}Service Definition
@EndUserText.label: 'Service Commandes"define service ZSB_Order { expose ZC_Order as Orders; expose ZC_OrderItem as OrderItems;}Behavior Definition
managed implementation in class zbp_i_order unique;strict ( 2 );
define behavior for ZI_Order alias Orderpersistent table zorderslock masterauthorization master ( instance ){ field ( readonly ) OrderId, CreatedBy, CreatedAt; field ( mandatory ) CustomerId;
create; update; delete;
action confirmOrder result [1] $self;
determination setDefaults on modify { create; } validation validateCustomer on save { field CustomerId; }
association _Items { create; }}Behavior Implementation
CLASS lhc_order DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS: get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations RESULT result,
confirmOrder FOR MODIFY IMPORTING keys FOR ACTION order~confirmOrder RESULT result,
setDefaults FOR DETERMINE ON MODIFY IMPORTING keys FOR order~setDefaults,
validateCustomer FOR VALIDATE ON SAVE IMPORTING keys FOR order~validateCustomer.ENDCLASS.
CLASS lhc_order IMPLEMENTATION.
METHOD get_instance_authorizations. " Verification des autorisations LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). APPEND VALUE #( %tky = <key>-%tky %op-%update = COND #( WHEN has_authority( ) = abap_true THEN if_abap_behv=>auth-allowed ELSE if_abap_behv=>auth-unauthorized ) ) TO result. ENDLOOP. ENDMETHOD.
METHOD confirmOrder. " Lire les commandes READ ENTITIES OF zi_order IN LOCAL MODE ENTITY order FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
" Mettre a jour le statut MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY order UPDATE FIELDS ( Status ) WITH VALUE #( FOR order IN lt_orders ( %tky = order-%tky Status = 'C' ) ).
" Retourner le resultat READ ENTITIES OF zi_order IN LOCAL MODE ENTITY order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result).
result = VALUE #( FOR order IN lt_result ( %tky = order-%tky %param = order ) ). ENDMETHOD.
METHOD setDefaults. READ ENTITIES OF zi_order IN LOCAL MODE ENTITY order FIELDS ( OrderDate Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY order UPDATE FIELDS ( OrderDate Status ) WITH VALUE #( FOR order IN lt_orders WHERE ( OrderDate IS INITIAL ) ( %tky = order-%tky OrderDate = cl_abap_context_info=>get_system_date( ) Status = 'O' ) ). ENDMETHOD.
METHOD validateCustomer. READ ENTITIES OF zi_order IN LOCAL MODE ENTITY order FIELDS ( CustomerId ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order). " Verifier le client SELECT SINGLE @abap_true FROM zcustomers WHERE customer_id = @ls_order-CustomerId INTO @DATA(lv_exists).
IF lv_exists = abap_false. APPEND VALUE #( %tky = ls_order-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Le client n existe pas' ) ) TO failed-order. ENDIF. ENDLOOP. ENDMETHOD.
ENDCLASS.Handler de requete OData V4
CLASS zcl_order_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_query_provider.ENDCLASS.
CLASS zcl_order_query IMPLEMENTATION.
METHOD if_rap_query_provider~select.
DATA: lt_orders TYPE TABLE OF zi_order.
" Lire le filtre DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ).
" Pagination DATA(lv_top) = io_request->get_paging( )->get_page_size( ). DATA(lv_skip) = io_request->get_paging( )->get_offset( ).
" Tri DATA(lt_sort) = io_request->get_sort_elements( ).
" Lire les donnees avec filtre dynamique SELECT * FROM zi_order WHERE customer_id IN @( VALUE #( FOR filter IN lt_filter WHERE ( name = 'CUSTOMERID' ) ( filter-range ) ) ) ORDER BY order_id INTO TABLE @lt_orders UP TO @lv_top ROWS OFFSET @lv_skip.
" Nombre total IF io_request->is_total_numb_of_rec_requested( ). SELECT COUNT(*) FROM zi_order INTO @DATA(lv_count). io_response->set_total_number_of_records( lv_count ). ENDIF.
io_response->set_data( lt_orders ).
ENDMETHOD.
ENDCLASS.Options de requete
| Option | Exemple | Description |
|---|---|---|
| $filter | $filter=Status eq 'O' | Filtrage |
| $select | $select=OrderId,Status | Selection de champs |
| $expand | $expand=_Items | Charger les navigations |
| $orderby | $orderby=OrderDate desc | Tri |
| $top | $top=10 | Limite |
| $skip | $skip=20 | Offset |
| $count | $count=true | Nombre total |
Bonnes pratiques
- Versionnement : Versions de service pour les breaking changes
- Gestion des erreurs : Statuts HTTP parlants et messages clairs
- Securite : Implementer les controles d’autorisation
- Performance : Utiliser $select, ne charger que les champs necessaires
- ETag : Verrouillage optimiste pour les mises a jour
- Preferer RAP : Pour les nouveaux developpements, RAP plutot que SEGW
Transactions importantes
| Transaction | Description |
|---|---|
| SEGW | Gateway Service Builder |
| /IWFND/MAINT_SERVICE | Activer le service |
| /IWFND/GW_CLIENT | Gateway Client (Test) |
| /IWFND/ERROR_LOG | Journal des erreurs |
Sujets connexes
- CDS Views - Core Data Services
- Developpement BAPI - APIs classiques
- RFC et Destinations - Appels distants
- Controles d’autorisation - Securite