Inhaltsverzeichnis
This is an old version of this page. To the new page please click here
Das ist eine alte Version dieser Seite. Zur neuen Seite klicken sie hier
This documentation shows an easy example how to provide a JSON webservice. There is also an example how to consume a JSON webservice.
There are several ways how to implement a webservice. This example shows how to use the GFMC (Generic Function Module Call) of the EPO connector in order to expose a function module as a JSON web service on a SAP server.
Function module Z_EPO_JSON_STRING
In this example, we implement an 'echo' webservice, which takes a string 'STRING' and an additional parameter 'REVERSE' to reverse the argument. The result is passed in the parameter 'RESULT'.
FUNCTION Z_EPO_JSON_STRING .
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(STRING) TYPE STRING
*" REFERENCE(REVERSE) TYPE FLAG
*" EXPORTING
*" REFERENCE(RESULT) TYPE STRING
*"----------------------------------------------------------------------
" string - webservice:
" return the string;
" if 'reverse' is not an empty string, reverse the string
DATA:
lv_reverse TYPE flag,
lv_char TYPE char1024.
" conversion to character in order to handle an string with spaces
lv_reverse = reverse.
IF lv_reverse IS INITIAL.
" simply copy:
result = string.
ELSE.
" reverse that string
" note: STRING_REVERSE does not handle strings..
lv_char = string.
CALL FUNCTION 'STRING_REVERSE'
EXPORTING
string = lv_char
lang = sy-langu
IMPORTING
RSTRING = lv_char
EXCEPTIONS
OTHERS = 0 " ignore errors
.
result = lv_char.
ENDIF.
ENDFUNCTION.
Parameters
For a clean interface, there are only importing and exporting parameters. We do not use: changing- / tables parameter and do not raise exceptions. The error handling should supply readable error messages into exporting parameter(s).
Note: usually, the importing/changing/tables/exporting parameters are mapped to separate JSON sub-objects. If there are only importing and exporting parameters, we can use a transformation in order to receive only importing parameters and to return only exporting parameters (see 'field mapping').
JSON request and response
The import parameters 'STRING' and 'REVERSE' should be posted in such a JSON structure:
{
"string":"test",
"reverse":"X"
}
The result should be returned in such a JSON structure:
{
"result":"tset"
}
SICF settings
In SICF, it is required to activate an element, which uses the EPO handler: activate the 'epo1soa' / 'jsonhandler'
..which uses the /EPO1/JSONHANDLER handler:
Logon data: supply the SAP-client and the language (otherwise, it would be necessary to specify the SAP-client as URL parameter). If there are multiple SAP-clients to maintain, it would be possible to define a separate SICF element for each client - or to specify the sap client as URL parameter (e.g. '?sap-client=100&sap-language=DE').
EPO Connector Customizing
With the definition of a service / operation, the EPO framework will be enabled to make a function module accessible as a webservice.
Start the transaction /EPO1/EXC, which is an area menu
Creation of number ranges for EPO Connector logging
If not exists, define two number range. For EPO services, we use the number range '00' for incoming services and '01' for outgoing services
Transaction /EPO1/EXC
- EPO Connector Configuration
- Maintain default number range /EPO1/NOR
Create the intervals '00' and '01'':
- 00: 0000100000000000 0000199999999999
- 01: 0000200000000000 0000299999999999
Creation of the EPO Connector Service Name
Transaction /EPO1/EXC
- EPO Connector Configuration
- In: Maintain EPO Runtime service configuration
Create a new service 'EPOFMJSON_STRING_REVERSE':
Important fields:
- Direction of service: this is an inbound service (called from outside)
- Operation mandatory: requires a defined operation (otherwise, ANY function module could be called by this service - which is not nice on a productive system)
- Default operation: allowes us to omit the specification of the operation name as URL parameter
Creation of the EPO Connector Operation
When using GFMC, the operation is mapped to the function module. Otherwise, the operation could also be mapped to another function module, using the menu entry GFMC: Change processing FM for given operation.
Transaction /EPO1/EXC
- EPO Connector Configuration
- Inbound Service Configuration
- In: Maintain EPO Runtime service configuration
Create a new operation 'Z_EPO_JSON_STRING'
Important fields:
- Number range object, Number range number: used to identify individual log lines
- Processing type: S - synchronuous, will be handled immediately
- Loggig of the EPO header (implicit), Message (in- and outgoing), Meta data (in- and outgoing)
- Processing FM: /EPO1/GFMC_JSON_PROCESSINGFM handles the JSON processing
- XSLT in: /EPO1/EXC_JSON_IMPORT_STRUC creates the 'IMPORT' JSON sub-object for input parameters
- XSLT out: /EPO1/EXC_JSON_EXPORT_STRUC removews the 'EXPORT' JSON sub-object from exporting parameters
- Field mapping: use the configurable field mapping (otherwise, all JSON field names will be in uppercase)
- Response format: t - using a transformation in order to create the JSON response
- Response strip: 1 - remove empty fields/table lines from the response
the API path..
We want now to create the API path, which is used to invoke the service.
- the first part is the 'server' part and depends on the visibility / accessability of Your SAP system: host name and port
- http or https, servername, port (if not the default port '80' for http, '443' for https)
- the second part is the service path in the SICF (or of the alias, if defined)
- the third part is the EPO service
- the last part is the EPO operation
Together we get something like
- http://my.server.com:80/epo1soa/jsonhandler/EPOFMJSON_STRING_REVERSE/Z_EPO_JSON_STRING
Field name mapping - /EPO1/FIELD_MAP
All the names of the JSON request / response has to be mapped to ABAP fields. To make the life easier, we will use the table /EPO1/FIELD_MAP in order to maintain the mapping. In our case, the field names are very simple, so a 'lowercase' format would be sufficient.
For this example, we will simply use the [lowercase] - mapping: every ABAP letter will be mapped to a lower-case JSON letter.
Note: when using the [camelCase] or [CamelCase] - mapping for inbound, use the camelCase - IMPORT - transformation /EPO1/EXC_JSON_IMPORT_STRUC_CC instead of /EPO1/EXC_JSON_IMPORT_STRUC. Otherwise, the 'IMPORT' - field name will be mapped to '_I_M_P_O_R_T' or 'I_M_P_O_R_T', which will not work.
Alternatively, You might map the INPUT field explicitely (possible, but not recommended):
Static field mapping
Sometimes, it is more convenient to have the mapping information directly in code instead of relying on customiziation.
The contents of the field name mapping - /EPO1/FIELD_MAP can also be defined in code, but the conversion ABAP <-> JSON has to be called manually (using the methods /EPO1/CL_TOOLS=>ABAP_TO_JSON or /EPO1/CL_TOOLS=>JSON_TO_ABAP).
further steps
Reduce JSON Output Data
Sometimes, an universal webservice will provide several completely different data structures (e.g. user data, invoice data, material data, ..).
This can be done by defining a large structure, which can hold each of the data to be delivered. However, the 'other' structure elements should not be returned.
Or, some 'initial' fields should be removed from the response (e.g. date fields, character strings, ..).
There are 2 implementations, wich are controlled by the response format in the service configuration table /EPO1/CONFIGOUT:
- dynamically reduce the output structure (implicit, a transformation is used)
- response format: 't', 'tb' or 'tbi'
- strip the generation of JSON elements (implicit, the function module /EPO1/EXC_ABAP_TO_JSON is used)
- response format: '0', '1', '2' or '10'
Please note, that the output of those both methods differ in some details (mapping of field names with '/', number- or date formatting).
Strip the output structure
Requirement: define one of the response formats: 't', 'tb' or 'tbi' Please note, that the stripping of output structures is limited to structures (und substructures); the inner content of a table cannot be stripped.
Define the export parameter ES_EPO1_ABAP_STRIP with the type /EPO1/EXC_ABAP_STRIP_STRUC. This structure contains all necessary parameters, which are used in the method /epo1/cl_tools=>abap_strip.
Meaning of the ABAP-STRIP - parameters:
- IT_KEEP: list of elements, which should be kept
- IT_STRIP: list of elements, which should be stripped (except, when listed in IT_KEEP)
- IV_STRIP_INITIAL: if 'X', remove all empty elements (structures, tables, primitive elements)
- IV_RECURSIVE: if 'X', recurse to all substructures
- IV_STRIP_EVERYTHING: strip all elements, except when listed in IT_KEEP
Note 1: elements and sub-elements are to be written as in ABAP (e.g. ELEMENT-SUB_ELEMENT-SUB_SUB_ELEMENT-...)
Note 2: the upper most element is the generated element 'EXPORT' for export parameters, 'CHANGING' for changing parameters and 'TABLES' for tables parameters. For simplicity, we expect, that only export parameters are used in a webservice - function module.
Note 3: it is not possible to change the contents of a table; only structure element might be stripped.
Example - export parameters of the function module for the webservice:
USER_DATA TYPE USR01 MATERIAL_DATA TYPE MARA MESSAGES TYPE BAPIRET2_T
remove everything except the user data:
es_epo1_abap_strip-iv_strip_everything = 'X'.
APPEND 'EXPORT-USER_DATA' TO es_epo1_abap_strip-it_keep.
Note: usually, we know exactly, which data are to be returned, so this is the preferred way to strip output data. This will work even if the webservice is enhanced in the future by returning additional data.
remove all empty structures from export:
es_epo1_abap_strip-iv_strip_initial = 'X'.
Note: this is maybe the most universal setting; leaving only structure elements, which has been filled
remove every empty element (recursive):
es_epo1_abap_strip-iv_strip_initial = 'X'.
es_epo1_abap_strip-iv_strip_recursive = 'X'.
Note: this stripping might be useful, when very large structures are returned, where most of the elements are initial and does not need to be returned. So we can remove all of the unused data. If there are elements, which should be returned, even when initial - then simply add the names of these elements to the IT_KEEP list.
remove only the material data (complete structure):
APPEND 'EXPORT-MATERIAL_DATA' TO es_epo1_strip-it_strip.
remove only the material number from the material data (maybe useless.. just so show, how it works):
APPEND 'EXPORT-MATERIAL_DATA-MATNR' TO es_epo1_strip-it_strip.
Strip the generation of JSON elements
Requirements:
- define one of the response formats: '0', '1', '2' or '10'
- check the checkbox 'Field mapping (even, if there is no customizing for field name mapping)
For details, please refer the section /EPO1/JSON_STRIP
beautify the API path
As seen before, the API path contains the EPO service (which could be defined with a lowercase name) and the operation, which is always to be written in uppercase.
The easy part is the usage of another EPO service: go back to the configuration of the EPO service, create a lowercase/camelcase - servicename (e.g. stringReverse) and a new operation.
The next step is to map the new service/operation to Your function module:
Transaction /EPO1/EXC: navigate to the item 'GFMC: Change processing FM for given operation'
and create a new entry with Your new service name, choose a new (lowercase/camelcase) operation name and Your existing function module name, e.g.:
- service: stringReverse
- operation: reverse
- version: (leave empty)
- function module: Z_EPO_JSON_STRING
Now, Your function module is (also) available with the new API name.
- old
- http://my.server.com:80/epo1soa/jsonhandler/EPOFMJSON_STRING_REVERSE/Z_EPO_JSON_STRING
- new
- http://my.server.com:80/epo1soa/jsonhandler/stringReverse/reverse
Recommendation: keep only one service/operation, delete the unused operation and then the unused service (then, the 'old' path will not work anymore).
use GFMC - special parameters
E.g. to read the request - http-headers, analyze the call path, etc., or to control the response. It is possible to specify HTTP headers and cookies.
Example:
...
*" REFERENCE(IT_EPO1_REQUEST_HEADERS) TYPE TIHTTPNVP
...
DATA:
ls_http_header LIKE LINE OF it_epo1_request_headers,
lt_path_elements TYPE TABLE OF string,
lv_path_element LIKE LINE OF lt_path_elements.
" get the absolute path
" use '~path_info_expanded' in order to get the relative path (starting after the SICF node name)
READ TABLE it_epo1_request_headers INTO ls_http_header WITH TABLE KEY name = '~path'.
SHIFT ls_http_header-value LEFT DELETING LEADING '/'.
SPLIT ls_http_header-value AT '/' INTO TABLE lt_path_elements.
enhance the logging by using FKEY1..FKEY4
Especially, when there is a lot of log entries, the usage of the foreign keys may help to filter for / find the right line of interest.
When calling the webservice, simply supply some key fields of interest. Preferrably - use the same FKEY field for the same kind of value, if there are multiple webservices used for similar data (like order creation, order change, order read ..) they should put the order number each into the same FKEY field.
Example:
...
lo_json->call_service(
EXPORTING
is_request_data = ls_request
iv_fkey1 = CONV #( iv_order_number )
iv_fkey2 = CONV #( iv_position_number )
IMPORTING
es_response_data = ls_response
et_response_headers = lt_response_headers
es_callstatus = ls_callstatus ).
...