EPO Consulting Wiki - EPO Connector - how to provide a simple JSON webservice


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'

ClipCapIt-200506-150450.PNG

..which uses the /EPO1/JSONHANDLER handler:

ClipCapIt-200506-150632.PNG


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':

ClipCapIt-200506-151935.PNG


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'

ClipCapIt-200506-152850.PNG


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.

ClipCapIt-200506-155743.PNG


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):

ClipCapIt-210921-161356.PNG


 

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'

ClipCapIt-220704-171332.PNG

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 ).

  ...