How To Create Rest Api and Apply MVC1 Routing

Hi,

In this blog post, I would like show how to create Rest api and how to apply MVC1 routing to handle different request simply from a controller class.

For that, first we will create handler and controller class for rest structure. Then we will add mvc1 controller class and model class to process business logic.

And finally we will create a service to hand rest requests.

To read more about SAP REST, have a look at REST Tutorial.

Let’s start.

Create following structures;

  • ZREST_S_RESP_STATE
  • ZREST_S_RESPONSE
  • ZREST_S_REQUEST
  • ZREST_S_RESP_STATE

  • ZREST_S_RESPONSE

  • ZREST_S_REQUEST

Now we will create classes.

Call Stack for a call

Classes

  • ZREST_CL_DEFS
  • ZREST_CL_MODEL
  • ZREST_CL_REQ_CONTROLLER
  • ZREST_CL_REQ_HTTP_HANDLER
  • ZREST_CL_HTTP_HANDLER
CLASS zrest_cl_defs DEFINITION PUBLIC CREATE PUBLIC . PUBLIC SECTION. CONSTANTS c_state_success TYPE char1 VALUE 'S' ##NO_TEXT. CONSTANTS c_state_warning TYPE char1 VALUE 'W' ##NO_TEXT. CONSTANTS c_state_error TYPE char1 VALUE 'E' ##NO_TEXT. PROTECTED SECTION. PRIVATE SECTION.
ENDCLASS. CLASS ZREST_CL_DEFS IMPLEMENTATION.
ENDCLASS.
CLASS zrest_cl_model DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS get_datetime EXPORTING !response_body TYPE zrest_s_response-body !state TYPE zrest_s_response-state . PROTECTED SECTION. PRIVATE SECTION.
ENDCLASS. CLASS ZREST_CL_MODEL IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZREST_CL_MODEL->GET_DATETIME
* +-------------------------------------------------------------------------------------------------+
* | [<---] RESPONSE_BODY TYPE ZREST_S_RESPONSE-BODY
* | [<---] STATE TYPE ZREST_S_RESPONSE-STATE
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD get_datetime. DATA: exref TYPE REF TO cx_root. TRY . TYPES : BEGIN OF ty_res, datetime TYPE tzntimestp, END OF ty_res. DATA: res TYPE ty_res. res-datetime = sy-datum && sy-uzeit. response_body = /ui2/cl_json=>serialize( EXPORTING data = res ). state-state = zrest_cl_defs=>c_state_success. CATCH cx_root INTO exref. state-state = zrest_cl_defs=>c_state_error. state-state_text = exref->get_text( ). ENDTRY. ENDMETHOD.
ENDCLASS.
CLASS zrest_cl_req_controller DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS process_request IMPORTING !req_json TYPE string EXPORTING !response_json TYPE string !response_stc TYPE zrest_s_response . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_req_get_datetime TYPE zrest_e_req_id VALUE '1001'. METHODS conv_stc_to_json IMPORTING response_stc TYPE zrest_s_response RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS ZREST_CL_REQ_CONTROLLER IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZREST_CL_REQ_CONTROLLER->CONV_STC_TO_JSON
* +-------------------------------------------------------------------------------------------------+
* | [--->] RESPONSE_STC TYPE ZREST_S_RESPONSE
* | [<-()] RESULT TYPE STRING
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD conv_stc_to_json. DATA: exref TYPE REF TO cx_root. TRY . result = /ui2/cl_json=>serialize( EXPORTING data = response_stc ). CATCH cx_root. result = exref->get_text( ). ENDTRY. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZREST_CL_REQ_CONTROLLER->PROCESS_REQUEST
* +-------------------------------------------------------------------------------------------------+
* | [--->] REQ_JSON TYPE STRING
* | [<---] RESPONSE_JSON TYPE STRING
* | [<---] RESPONSE_STC TYPE ZREST_S_RESPONSE
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD process_request. DATA: exref TYPE REF TO cx_root. TRY . DATA req_stc TYPE zrest_s_request. /ui2/cl_json=>deserialize( EXPORTING json = req_json CHANGING data = req_stc ). IF req_stc-id IS NOT INITIAL. DATA(model) = NEW zrest_cl_model( ). IF req_stc-id EQ c_req_get_datetime. model->get_datetime( IMPORTING response_body = response_stc-body state = response_stc-state ). ELSE. response_stc-state-state = zrest_cl_defs=>c_state_warning. MESSAGE s001(zrest_msg) INTO response_stc-state-state_text. ENDIF. ELSE. "Fill dummy content as sample req_stc-id = 999999. req_stc-body = 'Some Json Content'. response_stc-body = /ui2/cl_json=>serialize( EXPORTING data = req_stc ). response_stc-state-state = zrest_cl_defs=>c_state_warning. MESSAGE s002(zrest_msg) INTO response_stc-state-state_text. ENDIF. response_json = conv_stc_to_json( response_stc = response_stc ). CATCH cx_root. response_stc-state-state = zrest_cl_defs=>c_state_error. response_stc-state-state_text = exref->get_text( ). response_json = conv_stc_to_json( response_stc = response_stc ). ENDTRY. ENDMETHOD.
ENDCLASS.
CLASS zrest_cl_req_http_handler DEFINITION PUBLIC INHERITING FROM cl_rest_resource FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS if_rest_resource~get REDEFINITION . METHODS if_rest_resource~post REDEFINITION . PROTECTED SECTION. PRIVATE SECTION.
ENDCLASS. CLASS ZREST_CL_REQ_HTTP_HANDLER IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZREST_CL_REQ_HTTP_HANDLER->IF_REST_RESOURCE~GET
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD if_rest_resource~get. DATA(req_json) = mo_request->get_uri_query_parameter( iv_name = 'req' iv_encoded = abap_false ). DATA(controller) = NEW zrest_cl_req_controller( ). controller->process_request( EXPORTING req_json = req_json IMPORTING response_json = DATA(response_json) ). mo_response->create_entity( )->set_string_data( iv_data = response_json ). ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZREST_CL_REQ_HTTP_HANDLER->IF_REST_RESOURCE~POST
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_ENTITY TYPE REF TO IF_REST_ENTITY
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD if_rest_resource~post. DATA(req_json) = mo_request->get_entity( )->get_string_data( ). DATA(controller) = NEW zrest_cl_req_controller( ). controller->process_request( EXPORTING req_json = req_json IMPORTING response_json = DATA(response_json) ). mo_response->create_entity( )->set_string_data( iv_data = response_json ). ENDMETHOD.
ENDCLASS.

CSRF is disabled in handler below. Disabling it from GUI Parameters of Service does not work. You need to implement HANDLE_CSRF_TOKEN in order to disable it for rest.

class ZREST_CL_HTTP_HANDLER definition public inheriting from CL_REST_HTTP_HANDLER create public . public section. "Provides routing. Routing paths are assigned to controllers in this method methods IF_REST_APPLICATION~GET_ROOT_HANDLER redefinition . protected section. "If you want to disable, redefine that method. Just as an empty method. methods HANDLE_CSRF_TOKEN redefinition . PRIVATE SECTION.
ENDCLASS. CLASS ZREST_CL_HTTP_HANDLER IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZREST_CL_HTTP_HANDLER->HANDLE_CSRF_TOKEN
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_CSRF_HANDLER TYPE REF TO IF_REST_CSRF_HANDLER
* | [--->] IO_REQUEST TYPE REF TO IF_REST_REQUEST
* | [--->] IO_RESPONSE TYPE REF TO IF_REST_RESPONSE
* +--------------------------------------------------------------------------------------</SIGNATURE> method HANDLE_CSRF_TOKEN.
*CALL METHOD SUPER->HANDLE_CSRF_TOKEN
* EXPORTING
* IO_CSRF_HANDLER =
* IO_REQUEST =
* IO_RESPONSE =
* . endmethod. * <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZREST_CL_HTTP_HANDLER->IF_REST_APPLICATION~GET_ROOT_HANDLER
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RO_ROOT_HANDLER TYPE REF TO IF_REST_HANDLER
* +--------------------------------------------------------------------------------------</SIGNATURE> METHOD if_rest_application~get_root_handler. "Provides routing. "Service path /sap/bc/rest "Sample URL http://vhcalnplci:8000/sap/bc/rest/zrest/Rest?sap-client=001&req={"ID":1001,"BODY":"Some Json Content"} DATA(root_handler) = NEW cl_rest_router( ). root_handler->attach( EXPORTING iv_template = '/Rest' " Unified Name for Resources iv_handler_class = 'ZREST_CL_REQ_HTTP_HANDLER' " Object Type Name ). * "You can add more request handler classes
* "Service path /sap/bc/rest
* "Sample URL http://vhcalnplci:8000/sap/bc/rest/zrest/Rest2?sap-client=001&req={"ID":1001,"BODY":"Some Json Content"}
* root_handler->attach(
* EXPORTING
* iv_template = '/Rest2' " Unified Name for Resources
* iv_handler_class = 'ZREST_CL_REQ_HTTP_HANDLER2' " Object Type Name
* ). ro_root_handler = root_handler. ENDMETHOD.
ENDCLASS.

And final step, create a service.

Open SICF tcode and run.

Go to /sap/bc/rest and add new sub element

Add description and go to Handler List tab and our class, ZREST_CL_HTTP_HANDLER, as handler.

Activate Service. Right click service and click test. It will open browser. Change url to handle /Rest requests.

In my case, http://vhcalnplci:8000/sap/bc/rest/zrest/Rest

If you want to pass some params in get request, add query string parameters like ‘http://vhcalnplci:8000/sap/bc/rest/zrest/Rest?sap-client=001&req={“ID”:1001,”BODY”:”Some Json Content”}’

If you do not disable CSRF on handler, you will have issues calling non-get methods, such as POST methods from POSTMAN or from a client different than server itself.

Therefore in my example I have disabled CSRF.

Postman examples

Basic Authentication Parameters

Get Example and Result

Post Example and Result

That is all. In that way, you can integrate any environment, system to SAP.

Hope that helps.

Thanks for reading.

Related Links

https://help.sap.com/docs/SAP_NETWEAVER_740/753088fc00704d0a80e7fbd6803c8adb/0f5fb77942744afe94afafa78df57b70.html?locale=en-US&version=7.4.23

https://blogs.sap.com/2013/05/16/usage-of-the-abap-rest-library-sapbasis-740/

https://help.sap.com/docs/SAP_NETWEAVER_740/68bf513362174d54b58cddec28794093/b35c22518bc72214e10000000a44176d.html?locale=en-US&version=7.4.23