Структура и элементы web-сервиса по протоколу OData в ABAP
Для целей демонстрации OData-элементов обозначим следующую модель данных – это контейнер переменных данных (TVARVC-like), а также логи, связанные с изменением данных.
Содержание
OData: составные элементы, операции и опции
Представление бизнес-объекта в OData-проекте
Использование Navigation Property и inlining при помощи $expand
$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 таблицы:
- Заголовочная таблица с переменными
- Подчиненная таблица со значениями переменных
- Таблица с логами по изменению заголовочных данных и по изменению значений переменных
А также связанное хранилище файлов с переменной. Так называемый справочный файл с описанием и пояснением.
Этих трех таблиц будет достаточно, чтобы показать возможности; а также, чтобы не усложнять понимание. А бинарные данные данные также будем использовать для показа возможностей 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
ЗарегистрироватьсяУ вас уже есть учетная запись?
Войти