Меню

Структура и элементы web-сервиса по протоколу OData в ABAP

Для целей демонстрации OData-элементов обозначим следующую модель данных – это контейнер переменных данных (TVARVC-like), а также логи, связанные с изменением данных.

Часть 1.

Содержание

OData: составные элементы, операции и опции

Используемые бизнес-объекты

Представление бизнес-объекта в OData-проекте

CRUD-Q операции в OData

Q – query / запрос списка

R – read / чтение по ключу

C – create / создание записи

U – update/ обновление записи

D – delete / удаление записи

Использование Navigation Property и inlining при помощи $expand

Опции при выборке

$filter – ограничение выборки

$search – передача строки поиска

$select – выбор нужных полей с данными (чтобы получить список без излишеств)

$inlinecount – передача необходимости подсчета общего количества записей

$count – только количество строк в результирующей выборке

$orderby – передача параметров сортировки с клиента на сервер (Server-Side Sorting)

$expand – встраивание (inlining) дополнительных данных в выборку

$format – формат возвращаемых данных. xml json xlsx

$top $skip – пагинация средствами клиента (client-side pagination)

$skiptoken – пагинация средствами сервера (server-side pagination)

Массовая отправка запросов (batch-request) – принцип работы.

Метод DEFINE в *MPC для указания дополнительных аннотаций и свойств

Debug-Mode и Трассировка вызовов OData-запросов

sap-ds-debug=true : расширенный технический режим

Трассировка запрос и логирование ошибок

Заключение по разделу

OData: составные элементы, операции и опции

Используемые бизнес-объекты

Для целей демонстрации OData-элементов обозначим следующую модель данных – это контейнер переменных данных (TVARVC-like), а также логи, связанные с изменением данных.

С точки зрения базы данных имеется 3 таблицы:

  1. Заголовочная таблица с переменными
  2. Подчиненная таблица со значениями переменных
  3. Таблица с логами по изменению заголовочных данных и по изменению значений переменных

А также связанное хранилище файлов с переменной. Так называемый справочный файл с описанием и пояснением.

Этих трех таблиц будет достаточно, чтобы показать возможности; а также, чтобы не усложнять понимание. А бинарные данные данные также будем использовать для показа возможностей OData в части чтения и загрузки файлов.

Продемонстрируем модель данных задачи без OData.

Транзакция ZWEB_ABAP - Web ABAP: manage records.

В транзакции ведения мы можем назначать ID переменной, делать описание, указывать как отдельное значение, так и RANGE-значений; использовать переменную в качестве SWITCH, а также подгружать к ней файлы и, что очень важно смотреть логи изменения по каждому полю.

Также есть возможность включать точку останова на переменную и делать ее неактивной для использования, не удаляя содержимого.

Для использования подобного функционала внутри бизнес-приложений достаточно написать подобное:

Пример работы внутри прикладного бизнес-приложения (клиентская программа):

Результат: в программе нет хардкодных значений; места использования можно найти через инструмент ведения и включения режима отладки.

Теперь посмотрим, как будет выглядеть модель этого же объекта, но уже в OData-проекте.

Представление бизнес-объекта в OData-проекте

OData-проект ведется (создается, просматривается и изменяется) – в транзакции SEGW.

 Тестировать сервис можно/нужно в транзакции /IWFND/GW_CLIENT - SAP Gateway Client.

Составными частями информации про OData сервис являются:

1) Service document – набор сущностей, которые входят в конкретный web-Service.

2) Service metadata document – метаданные сервиса; это перечисление всех сущностей, с набором полей, Function Imports и другая подробная информация о свойствах сервисе (структура данных).

Составные элементы проекта OData (SAP Gateway Service Builder):

1) Entity (Сущность) и Entity Type (тип сущности). Набор полей для отображения информации. (по сути представляет из себя структуру со свойствами). Свойства полей сущности контролируются аннотациями. Аннотации задаются в классе-модели сервиса (в этом примере: ZCL_ZWEB_ABAP_DEMO1_MPC + ZCL_ZWEB_ABAP_DEMO1_MPC_EXT ). MPC – model provider class.

Детально аннотации описаны здесь (какая аннотация за что отвечает).

В каждом типе сущности есть ключ и он используется для ассоциаций (для связей между сущностями).

2) EntitySet (набор сущностей). Это массив сущностей, отношение 0:много.

У EntitySet также есть свойства, и они также регулируются через аннотации.

3) Property (свойство/поле) – аналог поля таблицы или отдельно стоящей переменной. 

4) Navigation Property (Свойство навигации).

Позволяет сделать переход от одной сущности сервиса к другой.

5) Association (Ассоциации). Определяет связь между сущности: кардинальность и поля, по которым идем связь.

В метаданных показано так:

CRUD-Q операции в OData

Для работы с объектами доступны 5 базовых операций, перечисленные в таблице:

Не обязательно все опции должны быть реализованы (имплементированы), но те операции, которые реализованы:

Рассмотрим операции и их минимальную реализацию на примере проекта ZWEB_ABAP_DEMO1.

Видим, что все операции проходят через класс ZCL_ZWEB_ABAP_DEMO1_DPC_EXT; именно в этом классе и реализуем в самых минимальных возможностях web-Service по протоколу OData.

Q – query / запрос списка

Запрос возвращает набор сущностей вызываемого типа

GET
/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet

METHOD varheadset_get_entityset.
**TRY.
*CALL METHOD SUPER->VARHEADSET_GET_ENTITYSET
*  EXPORTING
*    IV_ENTITY_NAME           =
*    IV_ENTITY_SET_NAME       =
*    IV_SOURCE_NAME           =
*    IT_FILTER_SELECT_OPTIONS =
*    IS_PAGING                =
*    IT_KEY_TAB               =
*    IT_NAVIGATION_PATH       =
*    IT_ORDER                 =
*    IV_FILTER_STRING         =
*    IV_SEARCH_STRING         =
**    io_tech_request_context  =
**  IMPORTING
**    et_entityset             =
**    es_response_context      =
*    .
** CATCH /iwbep/cx_mgw_busi_exception .
** CATCH /iwbep/cx_mgw_tech_exception .
**ENDTRY.


    DATA lt_var_set TYPE zttwa001_var_h_srv.
    FIELD-SYMBOLS <fs_var_set> TYPE zswa001_var_h_srv.

    DATA lt_var_id TYPE ztwa001_varid_tab.
    DATA lt_var_val TYPE ztwa001_varval_tab.
    DATA lt_var_file TYPE ztwa001_varfile_tab.

    FIELD-SYMBOLS <fs_var_id> TYPE ztwa001_varid.
    FIELD-SYMBOLS <fs_var_list> TYPE zswa001_variable_alv.

    SELECT * FROM ztwa001_varid
        INTO TABLE lt_var_id
    "    WHERE var_name IN mo_dto_scr->mt_var_name_rng
        UP TO 1000 ROWS
        .

    IF lt_var_id IS NOT INITIAL.
      SELECT * FROM ztwa001_varval
         INTO TABLE lt_var_val
         FOR ALL ENTRIES IN lt_var_id
         WHERE  var_name EQ lt_var_id-var_name
         .

      SELECT * FROM ztwa001_varfile
        INTO TABLE lt_var_file
        FOR ALL ENTRIES IN lt_var_id
         WHERE  var_name EQ lt_var_id-var_name
         .
    ENDIF.


    CLEAR lt_var_set.

    LOOP AT lt_var_id ASSIGNING <fs_var_id>.
      APPEND INITIAL LINE TO lt_var_set ASSIGNING <fs_var_set>.
      MOVE-CORRESPONDING <fs_var_id> TO <fs_var_set>.

      <fs_var_set>-name = <fs_var_id>-var_name.
      <fs_var_set>-description = <fs_var_id>-var_desc.
      <fs_var_set>-var_type = <fs_var_id>-var_type.

      <fs_var_set>-is_del = <fs_var_id>-is_del.
      <fs_var_set>-debug_is_on = <fs_var_id>-is_debug_on.
      <fs_var_set>-fast_val = <fs_var_id>-fast_val.
      <fs_var_set>-cru = <fs_var_id>-cru.
      <fs_var_set>-crd = <fs_var_id>-crd.
      <fs_var_set>-crt = <fs_var_id>-crt.
      <fs_var_set>-chu = <fs_var_id>-chu.
      <fs_var_set>-chd = <fs_var_id>-chd.
      <fs_var_set>-cht = <fs_var_id>-cht.

      <fs_var_set>-num_of_values = REDUCE i( INIT vals_in_var = 0
                                              FOR ls_var_val IN lt_var_val WHERE ( var_name = <fs_var_id>-var_name )
                                              NEXT vals_in_var = vals_in_var + 1 ).

      <fs_var_set>-num_of_files = REDUCE i( INIT files_in_var = 0
                                              FOR ls_var_file IN lt_var_file WHERE ( var_name = <fs_var_id>-var_name )
                                              NEXT files_in_var = files_in_var + 1 ).
    ENDLOOP.

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    et_entityset[] = lt_var_set[].
    es_response_context-count = lines( et_entityset[] ).
    es_response_context-is_sap_data_exists_calculated = abap_true.
    "es_response_context-skiptoken


  ENDMETHOD.

Система возвращает не только данные, но и ссылки, по которым можно (и нужно) обращаться к сущности по ключу (то есть перейти от Query к Read).

Используя эту ссылку, мы можем прочитать конкретную запись.

R – read / чтение по ключу

Запросы возвращает одну сущность-структуру. Вызов происходит по ключу.

GET
/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet('ZAPO_N15')
В скобках указывается ключ.

METHOD varheadset_get_entityset.
*        importing
*      !IV_ENTITY_NAME type STRING
*      !IV_ENTITY_SET_NAME type STRING
*      !IV_SOURCE_NAME type STRING
*      !IT_KEY_TAB type /IWBEP/T_MGW_NAME_VALUE_PAIR
*      !IO_REQUEST_OBJECT type ref to /IWBEP/IF_MGW_REQ_ENTITY optional
*      !IO_TECH_REQUEST_CONTEXT type ref to /IWBEP/IF_MGW_REQ_ENTITY optional
*      !IT_NAVIGATION_PATH type /IWBEP/T_MGW_NAVIGATION_PATH
*    exporting
*      !ER_ENTITY type ZCL_ZWEB_ABAP_DEMO1_MPC=>TS_VARHEAD
*      !ES_RESPONSE_CONTEXT type /IWBEP/IF_MGW_APPL_SRV_RUNTIME=>TY_S_MGW_RESPONSE_ENTITY_CNTXT
*    raising
*      /IWBEP/CX_MGW_BUSI_EXCEPTION
*      /IWBEP/CX_MGW_TECH_EXCEPTION .

    DATA lr_name_value_line TYPE REF TO /iwbep/s_mgw_name_value_pair.
    DATA ls_srv_in TYPE zswa001_var_h_srv.
    DATA ls_srv_out TYPE zswa001_var_h_srv.

    DATA lt_var_id TYPE ztwa001_varid_tab.
    DATA lt_var_val TYPE ztwa001_varval_tab.
    DATA lt_var_file TYPE ztwa001_varfile_tab.

    FIELD-SYMBOLS <fs_var_id> TYPE ztwa001_varid.
    FIELD-SYMBOLS <fs_var_list> TYPE zswa001_variable_alv.

    LOOP AT it_key_tab REFERENCE INTO lr_name_value_line.
      CASE lr_name_value_line->name.
        WHEN 'NAME' OR 'Name'.
          ls_srv_in-name = lr_name_value_line->value.
        WHEN OTHERS.

      ENDCASE.
    ENDLOOP.

    SELECT * FROM ztwa001_varid
        INTO TABLE lt_var_id
        WHERE var_name = ls_srv_in-name.

    SELECT * FROM ztwa001_varval
        INTO TABLE lt_var_val
        WHERE var_name = ls_srv_in-name.

    LOOP AT lt_var_id ASSIGNING <fs_var_id>.

      MOVE-CORRESPONDING <fs_var_id> TO ls_srv_out.

      ls_srv_out-name = <fs_var_id>-var_name.
      ls_srv_out-description = <fs_var_id>-var_desc.
      ls_srv_out-var_type = <fs_var_id>-var_type.

      ls_srv_out-is_del = <fs_var_id>-is_del.
      ls_srv_out-debug_is_on = <fs_var_id>-is_debug_on.
      ls_srv_out-fast_val = <fs_var_id>-fast_val.
      ls_srv_out-cru = <fs_var_id>-cru.
      ls_srv_out-crd = <fs_var_id>-crd.
      ls_srv_out-crt = <fs_var_id>-crt.
      ls_srv_out-chu = <fs_var_id>-chu.
      ls_srv_out-chd = <fs_var_id>-chd.
      ls_srv_out-cht = <fs_var_id>-cht.

      ls_srv_out-num_of_values = REDUCE i( INIT vals_in_var = 0
                                              FOR ls_var_val IN lt_var_val WHERE ( var_name = <fs_var_id>-var_name )
                                              NEXT vals_in_var = vals_in_var + 1 ).

      ls_srv_out-num_of_files = REDUCE i( INIT files_in_var = 0
                                              FOR ls_var_file IN lt_var_file WHERE ( var_name = <fs_var_id>-var_name )
                                              NEXT files_in_var = files_in_var + 1 ).

      EXIT.
    ENDLOOP.

    MOVE-CORRESPONDING ls_srv_out TO er_entity.

    IF ls_srv_out IS INITIAL.
      es_response_context-no_content = abap_true.
    ENDIF.

Структуру, которую система вернула по запросу GET (by key) можно использовать как образец для CREATE / UPDATE- запросов с помощью кнопки Use as Request.

C – create / создание записи

При создании используем метод POST и указываем имя Set.

В случае успеха система пришлет статус 201. С точки зрения ABAP-реализации этот статус означает, что не возникло исключительных ситуаций. Не факт, что данные обновились и/или корректно обновились.

POST
/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet
Тело запроса

<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://vhcalnplci:8000/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/" xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
 <id>http://vhcalnplci:8000/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet('ZAPO_N15')</id>
 <title type="text">VarHeadSet('ZAPO_N15')</title>
 <updated>2021-05-13T20:39:35Z</updated>
 <category term="ZWEB_ABAP_DEMO1_SRV.VarHead" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
 <link href="VarHeadSet('ZAPO_N15')" rel="self" title="VarHead"/>
 <link href="VarHeadSet('ZAPO_N15')/VarID2Values" rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/VarID2Values" type="application/atom+xml;type=feed" title="VarID2Values"/>
 <content type="application/xml">
  <m:properties>
   <d:Name>ZAPO_N15</d:Name>
   <d:Description>APO key Customers Number</d:Description>
   <d:VarType>3</d:VarType>
   <d:VarTypeTx/>
   <d:NumOfValues>3</d:NumOfValues>
   <d:NumOfFiles>0</d:NumOfFiles>
   <d:IsDel>false</d:IsDel>
   <d:DebugIsOn>false</d:DebugIsOn>
   <d:FastVal/>
   <d:Cru>DEVELOPER</d:Cru>
   <d:Crd>2021-05-11T00:00:00</d:Crd>
   <d:Crt>PT22H12M38S</d:Crt>
   <d:Chu>DEVELOPER</d:Chu>
   <d:Chd>2021-05-12T00:00:00</d:Chd>
   <d:Cht>PT14H08M02S</d:Cht>
  </m:properties>
 </content>
</entry>
  METHOD varheadset_create_entity.
*        importing
*      !IV_ENTITY_NAME type STRING
*      !IV_ENTITY_SET_NAME type STRING
*      !IV_SOURCE_NAME type STRING
*      !IT_KEY_TAB type /IWBEP/T_MGW_NAME_VALUE_PAIR
*      !IO_TECH_REQUEST_CONTEXT type ref to /IWBEP/IF_MGW_REQ_ENTITY_C optional
*      !IT_NAVIGATION_PATH type /IWBEP/T_MGW_NAVIGATION_PATH
*      !IO_DATA_PROVIDER type ref to /IWBEP/IF_MGW_ENTRY_PROVIDER optional
*    exporting
*      !ER_ENTITY type ZCL_ZWEB_ABAP_DEMO1_MPC=>TS_VARHEAD
*    raising
*      /IWBEP/CX_MGW_BUSI_EXCEPTION
*      /IWBEP/CX_MGW_TECH_EXCEPTION .
    DATA ls_srv_in TYPE zcl_zweb_abap_demo1_mpc=>ts_varhead.

    io_data_provider->read_entry_data( IMPORTING es_data = ls_srv_in ).
    MOVE-CORRESPONDING ls_srv_in TO er_entity.

  ENDMETHOD.

Видим, что важным (и довольно понятным с точки зрения использования в своей реализации) является чтение входных параметров через
 io_data_provider->read_entry_data( IMPORTING es_data = ls_srv_in ).

U – update/ обновление записи

При создании указываем метод PUT или PATCH, а также ключ записи. Успешным ответом является 204. Ответ ( response) приходит без данных.

PUT – при указании тех полей, которые требуется обновить (не обязательно указывать все поля).
PATCH – в случае указания части поле система сначала прочитает запись через GET_ENTITY (READ), то есть дообогатит запись; а потом запустит метод UDPATE. 

Можно переопределить поведение через method redifinition
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~PATCH_ENTITY

/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet('ZAPO_N15')
Тело запроса

<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://vhcalnplci:8000/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/" xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
 <id>http://vhcalnplci:8000/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet('ZAPO_N15')</id>
 <title type="text">VarHeadSet('ZAPO_N15')</title>
 <updated>2021-05-13T20:39:35Z</updated>
 <category term="ZWEB_ABAP_DEMO1_SRV.VarHead" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
 <link href="VarHeadSet('ZAPO_N15')" rel="self" title="VarHead"/>
 <link href="VarHeadSet('ZAPO_N15')/VarID2Values" rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/VarID2Values" type="application/atom+xml;type=feed" title="VarID2Values"/>
 <content type="application/xml">
  <m:properties>
   <d:Name>ZAPO_N15</d:Name>
   <d:Description>APO new text</d:Description>
  </m:properties>
 </content>
</entry>
  METHOD varheadset_update_entity.
**TRY.
*CALL METHOD SUPER->VARHEADSET_UPDATE_ENTITY
*  EXPORTING
*    IV_ENTITY_NAME          =
*    IV_ENTITY_SET_NAME      =
*    IV_SOURCE_NAME          =
*    IT_KEY_TAB              =
**    io_tech_request_context =
*    IT_NAVIGATION_PATH      =
**    io_data_provider        =
**  IMPORTING
**    er_entity               =
*    .
** CATCH /iwbep/cx_mgw_busi_exception .
** CATCH /iwbep/cx_mgw_tech_exception .
**ENDTRY.

    DATA ls_srv_in TYPE zcl_zweb_abap_demo1_mpc=>ts_varhead.

    io_data_provider->read_entry_data( IMPORTING es_data = ls_srv_in ).
    MOVE-CORRESPONDING ls_srv_in TO er_entity.

  ENDMETHOD.

D – delete / удаление записи

При создании указываем метод DELETE, а также ключ записи. Успешным ответом является 204. Ответ ( response) приходит без данных.

DELETE 
/sap/opu/odata/SAP/ZWEB_ABAP_DEMO1_SRV/VarHeadSet('ZAPO_N15')

METHOD varheadset_delete_entity.
*    importing
*      !IV_ENTITY_NAME type STRING
*      !IV_ENTITY_SET_NAME type STRING
*      !IV_SOURCE_NAME type STRING
*      !IT_KEY_TAB type /IWBEP/T_MGW_NAME_VALUE_PAIR
*      !IO_TECH_REQUEST_CONTEXT type ref to /IWBEP/IF_MGW_REQ_ENTITY_D optional
*      !IT_NAVIGATION_PATH type /IWBEP/T_MGW_NAVIGATION_PATH
*    raising
*      /IWBEP/CX_MGW_BUSI_EXCEPTION
*      /IWBEP/CX_MGW_TECH_EXCEPTION .

    DATA lr_name_value_line TYPE REF TO /iwbep/s_mgw_name_value_pair.
    DATA ls_srv_in TYPE zcl_zweb_abap_demo1_mpc=>ts_varhead.

    LOOP AT it_key_tab REFERENCE INTO lr_name_value_line.
      CASE lr_name_value_line->name.
        WHEN 'NAME' OR 'Name'.
          ls_srv_in-name = lr_name_value_line->value.
        WHEN OTHERS.

      ENDCASE.
    ENDLOOP.

  ENDMETHOD.

Мы рассмотрели базовые операции и их минимальную реализацию; а теперь рассмотрим базовые опции.

Использование Navigation Property и inlining при помощи $expand

Navigation Property – это свойство, обеспечивающее связь между одной и другой сущностью.

В SEGW-проекте (GateWay-проект) его можно наблюдать в описаниях свойства сущностей

В metadata

Использование Navigation Property позволяет вернуть (выбрать)

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти