Exposing service / REST APIs from SAP – 2

While working in Real time, we may have to expose N numbers of APIs from SAP and it is not a good practice to create a node in SICF for each requirement.

To make it dynamic, we can add identifiers in the API / URL. In SAP, we’ll be creating a Table (Z table, e.g: ZHTTPHANDLER ) which will have the entries maintained for this Identifier along with respected class and method name. This class / method will be having the business logic. For different requirement, we can create another method in same or different class, and we need to maintain these class methods with identifiers in the table.

API: https://sap-abcde.fg.hijkl.com:8800/sap/bc/ztestm1/automation/get_sku_details

Here identifiers ‘/automation/get_sku_details’ have been added in the URL.

in SAP, Table ZHTTPHANDLER is created and maintained with identifiers.

TCode SICF → Navigate to Path: /default_host/sap/bc/ → Service handler class: ZCL_HTTPHANDLER is maintained in Service: ZTESTM1

Include Interface: IF_HTTP_EXTENSION

Attributes:

Method: IF_HTTP_EXTENSION~HANDLE_REQUEST and it’s Parameter

Source code of Method:

  METHOD if_http_extension~handle_request.

    DATA : _respons_data      TYPE string,
           _http_status_code  TYPE i,
           _http_status_text  TYPE string,
           _http_content_type TYPE string.


    _http_server  = server.

    TRY.

* Request data / Payload
        DATA(_request_data) = server->request->get_cdata( ).
* Request Method
        DATA(_method)       = server->request->get_header_field( if_http_header_fields_sap=>request_method ).
* Parameters
        DATA(_parameters)   = server->request->get_header_field( if_http_header_fields_sap=>query_string ).
* Service Path
        DATA(_service_path) = server->request->get_header_field( if_http_header_fields_sap=>path_info ).
        SPLIT _service_path+1 AT '/' INTO DATA(_application) DATA(_process).
        _application = |{ _application CASE = (cl_abap_format=>c_upper) }|.
        _process     = |{ _process CASE = (cl_abap_format=>c_upper) }|.
* Check if Application / Process is onboarded - and get the Class / Method name
        SELECT SINGLE * FROM zhttphandler INTO @DATA(_s_zhttphandler)
          WHERE application  EQ @_application
            AND process_name EQ @_process.
        IF sy-subrc IS INITIAL AND _s_zhttphandler-class_name IS NOT INITIAL AND _s_zhttphandler-method_name IS NOT INITIAL.

          CALL METHOD (_s_zhttphandler-class_name)=>(_s_zhttphandler-method_name)
            EXPORTING
              _o_request         = server->request
              _input_data        = _request_data
            IMPORTING
              respons_data_      = _respons_data
              http_status_code_  = _http_status_code
              http_status_text_  = _http_status_text
              http_content_type_ = _http_content_type.

* Return Response
          DATA(o_response_) = _http_server->response.
          o_response_->set_status( code = _http_status_code reason = _http_status_text ).
          o_response_->set_content_type( _http_content_type ).

          o_response_->set_cdata( _respons_data ).

        ELSE.
* If Application / Process is not onboarded - then Return 501 with Not Implemented
          o_response_ = _http_server->response.
          o_response_->set_status( code = '501' reason = 'Not Implemented' ).

          _respons_data = '<response><faultstring>Service Not Implemented</faultstring></response>'.
          o_response_->set_cdata( _respons_data ).
        ENDIF.

      CATCH cx_root INTO DATA(_o_exception).
        _http_server->response->set_status( code = 500 reason = 'Internal Server Error' ).
    ENDTRY.

  ENDMETHOD.

Another class: ZCL_TEST_AUTOMATION is having business logic in its Methods, which will be called from ZCL_HTTPHANDLER as it is maintained in Table ZHTTPHANDLER.

Class and it’s attributes:

Method and Parameters:

Source code of Method:

  METHOD service1_execute.

    TYPES : BEGIN OF ty_materials,
              material_number TYPE matnr,
            END OF ty_materials,

            BEGIN OF ty_error,
              msgtype TYPE msgtyp,
              message TYPE char100,
            END OF ty_error.

    DATA : _t_materials TYPE STANDARD TABLE OF ty_materials,
           _t_error     TYPE STANDARD TABLE OF ty_error.

    FIELD-SYMBOLS : <fs_out_table> TYPE ANY TABLE.

* Parse input Json to Internal Table
    CALL METHOD /ui2/cl_json=>deserialize
      EXPORTING
        json             = _input_data
        pretty_name      = /ui2/cl_json=>pretty_mode-camel_case
        conversion_exits = 'X'
      CHANGING
        data             = _t_materials.
    IF _t_materials[] IS NOT INITIAL.
      LOOP AT _t_materials ASSIGNING FIELD-SYMBOL(<fs_materials>).
        <fs_materials>-material_number = |{ <fs_materials>-material_number ALPHA = IN }|.
      ENDLOOP.

* Fetch and Populate output table
      SELECT a~matnr, a~mtart, a~matkl, a~meins, b~maktx
        FROM mara AS a INNER JOIN makt AS b
        ON a~matnr EQ b~matnr
        INTO TABLE @DATA(_t_mat_details)
        FOR ALL ENTRIES IN @_t_materials
        WHERE a~matnr EQ @_t_materials-material_number
          AND b~spras EQ 'E'.
      IF sy-subrc IS INITIAL.
        ASSIGN _t_mat_details[] TO <fs_out_table>.
      ELSE.
        _t_error = VALUE #( BASE _t_materials ( msgtype = 'E' message = 'No data found' ) ).
        ASSIGN _t_error[] TO <fs_out_table>.
      ENDIF.
    ENDIF.

* Internal Table to Json
    IF <fs_out_table> IS ASSIGNED.
      CALL METHOD /ui2/cl_json=>serialize
        EXPORTING
          data        = <fs_out_table>
          compress    = abap_false
          pretty_name = /ui2/cl_json=>pretty_mode-camel_case
        RECEIVING
          r_json      = respons_data_.
    ENDIF.

    http_status_code_  = _http_status_code.
    http_status_text_  = _http_status_text.
    http_content_type_ = _content_type_json.

  ENDMETHOD.

Now we are ready with the API / Service, before delivering it we must Test it using Postman or any other tool.

Business logic Class and Method name is picked at runtime as per Identifiers.

Input Json is parsed and Internal table _T_MATERIALS is populated.

Service executed successfully from Postman

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s