SAP services are customized in transaction SICF. An example is the service path /default_host/epo1soa/jsonapphandler with the handler class /EPO1/JSONHANDLER. This SICF handler accepts requests in JSON format and returns responses in JSON. For XML, use the handler class /EPO1/XMLHANDLER, like used in /default_host/epo1soa/xmlhandler
According to the EPO customizing, the correct function module name will be selected according to the given (called) combination of EPO service and EPO operation.
When called, a dynamic function module handler - report will be generated (when necessary) - and invokes the function module. This dynamically generated report will be re-created, if the called function module has been changed since the last generation. So the call will usually be very fast.
The function module to be called should preferrably use only 'importing' and 'exporting' parameters; try to avoid 'changing' and 'tables', as they are not well suited to map the usage of a usual web service call. Do not raise exceptions, but return meaningful error messages to the user.
Maybe, it might be useful to write a wrapper function module to an existing function module, which handles all errors and returns error messages in case of errors.
The processing function module (configured in each operation) controls the required request format.
If both formats are required, 2 separate combination of service/operation has to be configured. So Your webservice can support multiple data formats with the same logic (=function module) behind.
The request (=incoming data) is a structure with the main elements 'IMPORT', 'CHANGING' and 'TABLES'.
The response (=outgoing data) is a structure with the main elements 'EXPORT', 'CHANGING' and 'TABLES'.
Each main element has (structured or primitive) child-elements according to the defined parameters of each category. There are a few exceptions for special EPO - parameters, which are neither mapped to the request nor to the response ( see below for details ).
Suppose a function module with
The JSON request should look like:
{
"IMPORT": {
"IV_STRING":"some string",
"IV_INT":5,
"IS_STRUC": {
"NAME":"some_name",
"VALUE":"some_value"
}
}
}
The JSON response would look like:
{
"EXPORT": {
"EV_STRING":"some result",
"EV_INT":42,
"ET_STRING": [
"string_one",
"string_two",
"last_string"
]
}
}
The XML request should look like (SAP - ASX-XML):
<?xml version="1.0" encoding="utf-8"?>
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
<IMPORT>
<IV_STRING>some string</IV_STRING>
<IV_INT>5</IV_INT>
<IS_STRUC>
<NAME>some_name</NAME>
<VALUE>some_value</VALUE>
</IS_STRUC>
</IMPORT>
</asx:values>
</asx:abap>
The XML response would look like:
<?xml version="1.0" encoding="utf-8"?>
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
<asx:values>
<EXPORT>
<EV_STRING>some result</EV_STRING>
<EV_INT>42</EV_INT>
<ET_STRING>
<item>string_one</item>
<item>string_two</item>
<item>last_string</item>
</ET_STRING>
</EXPORT>
</asx:values>
</asx:abap>
Please note, that only those request - elements are passed to the function modules, which have matching input/changing/tables parameters.
Some special named parameters are not mapped to request/response, but are used to pass special data to/from the called function module. These parameters are truely optional, so it is not neccessary to specify them (except, if You want to use them!).
..of type TIHTTPNVP is used to pass the HTTP headers of the request into the function module.
Example to extract the API path:
DATA:
ls_http_header LIKE LINE OF it_epo1_request_headers,
lt_path_elements TYPE /epo1/xpath_t.
" extract the path from the request headers
READ TABLE it_epo1_request_headers INTO ls_http_header WITH TABLE KEY name = '~path_info_expanded'.
SHIFT ls_http_header-value LEFT DELETING LEADING '/'.
SPLIT ls_http_header-value AT '/' INTO TABLE lt_path_elements.
" first path_element: usually the service name
" second path_element: usually the operation
" third path_element: could contain same parameter values..
..of type /EPO1/GFMC_XMLHEAD_KEY contains the key fields of the EPO log (table /EPO1/XMLHEAD) of the current webservice call.
It might be useful in order to
..of type /EPO1/EXC_ABAP_STRIP_STRUC. This very special parameter can be used to dynamically strip some elements out of the response structure.
Suppose a REST webservice, which is able to return some very different kind of data:
All these data has to be supplied in the EXPORT parameters of the function module. However, only a part of the response is filled, depending on the usage of the function module.
With the parameter /EPO1/EXC_ABAP_STRIP_STRUC, the function module is able to control, which data is to be removed from the response. See the detailed description, how to use this parameter.
..of type I; the function module may set the HTTP code in order to signal success or failure - states, without returning any data.
..of type STRING; here, the function moduly might supply the descriptive text to the HTTP code.
Note: by default, the GFMC-framework will supply the HTTP reason by default from the EV_EPO1_HTTP_CODE, using the mapping of the interface IF_HTTP_STATUS. So it is usually NOT necessary to use and fill this parameter.
..of type TIHTTPNVP is used to pass the HTTP headers to the response.
..of type /EPO1/EXC_COOKIE_TAB is used to pass cookies via HTTP headers to the response.
Note: for incoming messages, the class /EPO1/CL_COOKIES can be used to extract cookie-information from the HTTP headers.
..of type /EPO1/STATUS; the function module may set the status for the response in message logging. The default value is 50. For monitoring purposes we recommend using the status of the request (see EV_EPO1_REQUEST_STATUS), because the request is always available. Even in case of syntax error or of wrong request payload.
..of type /EPO1/STATUS; The function module may set the status for the request message. Any numeric value between 00 and 99 can be used.
The following values are used by default:
Message status 51 will be set in the following cases automatically
In all those cases there will be an automated response in XML or JSON format created by the EPO Connector.
..of type /EPO1/GFMC_XMLHEAD_FKEYS, containing the 4 foreign-key fields for the logging of the webservice response.
Important data (order numbers, etc.) can be put into one of those 4 fields, which are then stored in the EPO log. Later, a log entry can be found easier for a given webservice call.
In order to support JSON field names other than the uppercase-variant of the ABAP field names, a name mapping can be switched-on in the EPO customizing of the operation:
Select 'Field Mapping' and the response format 't' (with transformation - not supported in old releases) or '0' (using a function module to create the JSON response).
The conversion schema has to be customized - see the description of table /EPO1/FIELD_MAP for details.
As described above, the request has to contain the main structure 'IMPORT', while the response contains the main structure 'EXPORT'. To get rid of those main structures, specify following transformations in the EPO customizing of the operation:
The transformation /EPO1/EXC_JSON_IMPORT_STRUC will generate an IMPORT-structure as wrapper over all input parameters. Use the transformation /EPO1/EXC_JSON_IMPORT_STRUC_CC, if the field name mapping '[camelCase]' or '[CamelCase]' is used.
The transformation /EPO1/EXC_JSON_EXPORT_STRUC will remove the EXPORT-structure and leave all child elements as root-elements of the response. The removal of the EXPORT-structure happens after the field name mapping. Our transformation handles lower-case and camel-case mappings. Only, if the name of the EXPORT-element has been mapped to a very different name, You would need to write a special transformation for that case.
Compare the now valid JSON structurs against the original ones (above) - examples without field name mapping.
The JSON request should now look like:
{
"IV_STRING":"some string",
"IV_INT":5,
"IS_STRUC": {
"NAME":"some_name",
"VALUE":"some_value"
}
}
The JSON response would now look like:
{
"EV_STRING":"some result",
"EV_INT":42,
"ET_STRING": [
"string_one",
"string_two",
"last_string"
]
}
The format of the XML can be changed, using transformations (see transaction STRANS.
There are 2 prepared transformations in order to use a WSDL - conform style. Other transformations could be written and customized, in order to target any other XML format - and/or to implement a mapping for field names.
The transformation /EPO1/SOAP_GFMC_TO_ASXML converts a SOAP - styled request-XML into the required AS-XML, wile the transformation /EPO1/ASXML_TO_SOAP_GFMC reverses the AS-XML format into a SOAP - format for the response.
The XML request should now look like (the name of the tag 'OPERATION_NAME' might be choosen free):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<OPERATION_NAME>
<IMPORT>
<IV_STRING>some string</IV_STRING>
<IV_INT>5</IV_INT>
<IS_STRUC>
<NAME>some_name</NAME>
<VALUE>some_value</VALUE>
</IS_STRUC>
</IMPORT>
</OPERATION_NAME>
</soap:Body>
</soap:Envelope>
The XML response would now look like:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:asx="http://www.sap.com/abapxml" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<OPERATION_NAMEResponse xmlns="http://www.epoconsulting.com/gfmc/OPERATION_NAME">
<EXPORT>
<EV_STRING>some result</EV_STRING>
<EV_INT>42</EV_INT>
<ET_STRING>
<item>string_one</item>
<item>string_two</item>
<item>last_string</item>
</ET_STRING>
</EXPORT>
</OPERATION_NAMEResponse>
</soap:Body>
</soap:Envelope>