SAP oData Service [GET]: Consume REST Service
Overview
- In this blog, we try to consume a REST-Services in an OData-Service.
- Here we use GET operation of OData-Service
- and while consuming the REST-Service, we use HTTP-Method as POST to send request message and in acknowledgement REST-Service provides response which will be mapped to OData-Service’s structure.
- This blogs is a business case example of parent blog:
Business Scenario:
- In a fiori-app, where data need to be accessed from a non-sap system.
- This non-sap system has a REST-Service technique for data exchange to out-side world.
- So, in fiori-App, we need an oData-Service which can consume/talk to non-sap’s REST-Service for data Exchange.
- Note: Here we can consume REST-Services directly in fiori-App, but we recommend to access this via oData-Service, due to following reasons:
- The Cross-domain issue in JavaScript codes while accessing REST-Services when we deploy fiori-App in Fiori-Server.
REST Service Details:
- This REST-Service of non-sap system is a medium for data exchange between SAP-FioriApp and non-sap system
- REST (REpresenational State Transfer) is an architectural style that uses simple and lightweight mechanism for inter-machine communication.
- In this blog’s example, this REST-Service has following components for data-exchange:
- EndPoint-URL:
- This consists of http(s), host, port and RestProjectPath of Non-SAP system
- For Example:
- http://<ServiceIP>:<Port>/<REST_Service_Path>
- Access-credentials:
- user-id and password which is required to access this REST service
- Request-Message:
- This is the input format which respective REST-Service understands, for which it runs its program logic and returns data accordingly.
- Here, below is the JASON input format:
-
[ { "taskId":286079 }, { "taskId":287571 } ]
- Response-Message:
- This is the output message format which we receive from REST-Service in acknowledgement.
- Here, output format is like:
-
{ "response":"Success", "responseData":{ "response":"Posted successfully" }, "error":null, "errorList":[] }
- EndPoint-URL:
- REST Services are working with ‘Resources’ instead of ‘Operations’
Steps to create oData Service [with GET_ENTITYSET]:
- To Consume above REST-Service, we implement oData-Service in following manner:
- Creation of an EntitySet which will have two Property:
- REQUEST
- To send JSON-Input in given REST-Service format
- RESPONSE
- To receive JSON-output from REST-Service.
- REQUEST
- Re-Definition of method ‘GET_ENTITYSET‘ of respective Enity, where we write ABAP-Code to consume REST-Service.
- Creation of an EntitySet which will have two Property:
- Following detailed Steps can be followed for same:
[1] Create an EntityType and EntitySet
- To Create an OData-Serivice, go-to SAP-Firoi-Server t-code ‘SEGW’
- Right click on Project’s folder ‘Entity Types’ to create an Entity
- with Name ‘ConsumeREST’ with EntitySet name ‘ConsumeRESTSet’
- Create two properties (REQUEST and RESPONSE) and make REQUEST as key.
- Save and ‘Generate Runtime object’ by clicking circle icon (red/white) with project selection.
- Thus meta-structure for EntitySet gets created.
[2] Redefine method ‘ConsumeRESTSet_GET_ENTITYSET‘
- Next we need to re-define method ‘GET_ENTITYSET‘ where abap-code will be written to consume REST service. This method will have following functionalities:
- Get input from property ‘REQUEST’ of ODataService’s EntitySet ‘ConsumeRESTSet’
- Consume/trigger REST-Service with this input
- In acknowledgement, REST-Service provide response, extract it and map it to oDataService’s EntitySet ‘ConsumeRESTSet’ in property ‘RESPONSE’
- To re-define same, go to Odata-Service project’s folder ‘Runtiime Artifacts’ -> select ‘DPC_EXT’ class -> double click on it to go to abap runtime workbench
- Below is ‘_DPC_EXT’ class, where we re-define ‘GET_ENTITYSET‘
- Go to folder ‘_DPC_EXT’ -> folder ‘Methods’ -> folder ‘Inherited Methods’
- select method ‘ConsumeRESTSet_GET_ENTITYSET‘ -> right click -> select ‘Redefine’
- Once method redefined, it get listed inside folder ‘Methods/Redefinitions’
- and we write abap-code iside this method to do following tasks:
- To read input from property ‘REQUEST’ of ODataService’s EntitySet ‘ConsumeRESTSet’
- Call REST-Service by sending input and using its endpoint url
- In acknowledgement, REST-Service provide response,
- extract it and map it to oDataService’s EntitySet ‘ConsumeRESTSet’ in property ‘RESPONSE’
- Please note:
- We can not do it in redefinition of GET_ENTITY, because get_filter does not accessible inside it.
- Below is the screen of method ‘ConsumeRESTSet_GET_ENTITYSET‘
- And ABAP code written inside method ‘ConsumeRESTSet_GET_ENTITYSET‘ is as follows:
-
method CONSUMERESTSET_GET_ENTITYSET. DATA: lt_filters TYPE /iwbep/t_mgw_select_option, ls_filter TYPE /iwbep/s_mgw_select_option, ls_so TYPE /iwbep/s_cod_select_option, LS_CONSUMERESTSET TYPE ZCL_ZTEST_ODATA_MPC=>TS_CONSUMEREST. DATA: lv_RestSrvUrl TYPE STRING, "Var for Http REST Service Url lv_HTTP_CLIENT TYPE REF TO IF_HTTP_CLIENT, "Var for Rest Http Client LV_REQUEST TYPE STRING, LV_RESPONSE TYPE STRING. * Initiating filter to Read input from EnitySet of ODataService lt_filters = io_tech_request_context->get_filter( )->get_filter_select_options( ). * Extract input from Property 'REQUEST' of EntitySet 'CONSUMERESTSet' Clear ls_filter. READ TABLE lt_filters WITH TABLE KEY property = 'REQUEST' INTO ls_filter. IF sy-subrc EQ 0. READ TABLE ls_filter-select_options INTO ls_so index 1. IF sy-subrc EQ 0. LV_REQUEST = ls_so-low. ENDIF. ENDIF. * REST Service URL lv_RestSrvUrl = 'http://<ServiceIP>:<Port>/<REST_Service_Path>'. *Steps to Call REST Service =================================== *STEP-1 : CREATE HTTP CLIENT CALL METHOD CL_HTTP_CLIENT=>CREATE_BY_URL EXPORTING URL = lv_RestSrvUrl IMPORTING CLIENT = lv_HTTP_CLIENT EXCEPTIONS ARGUMENT_NOT_FOUND = 1 PLUGIN_NOT_ACTIVE = 2 INTERNAL_ERROR = 3 OTHERS = 4 . *STEP-2 : AUTHENTICATE HTTP CLIENT CALL METHOD LV_HTTP_CLIENT->AUTHENTICATE EXPORTING USERNAME = 'Service_user-id' PASSWORD = 'Service_password'. *STEP-3 : Set headers for REST Service Request Call CALL METHOD lv_HTTP_CLIENT->REQUEST->SET_HEADER_FIELD EXPORTING NAME = '~request_method' VALUE = 'POST'. CALL METHOD lv_HTTP_CLIENT->REQUEST->SET_HEADER_FIELD EXPORTING NAME = 'Content-Type' VALUE = 'application/json; charset=utf-8'. CALL METHOD lv_HTTP_CLIENT->REQUEST->SET_HEADER_FIELD EXPORTING NAME = 'Accept' VALUE = 'application/json, text/html'. * STEP-3.1 : Attach Request Message CALL METHOD LV_HTTP_CLIENT->REQUEST->SET_CDATA EXPORTING DATA = LV_REQUEST OFFSET = 0. *STEP-4 : SEND HTTP REQUEST CALL METHOD lv_HTTP_CLIENT->SEND EXCEPTIONS HTTP_COMMUNICATION_FAILURE = 1 HTTP_INVALID_STATE = 2. *STEP-5 : GET HTTP RESPONSE CALL METHOD lv_HTTP_CLIENT->RECEIVE EXCEPTIONS HTTP_COMMUNICATION_FAILURE = 1 HTTP_INVALID_STATE = 2 HTTP_PROCESSING_FAILED = 3. *STEP-6 : Extract Rest-Service-Response CLEAR LV_RESPONSE. LV_RESPONSE = lv_HTTP_CLIENT->response->get_cdata( ). *STEP-7 : Appending Rest-Service-Response to EntitySet of ODataService CLEAR LS_CONSUMERESTSET. LS_CONSUMERESTSET-REQUEST = LV_REQUEST. "Append Sent Request LS_CONSUMERESTSET-RESPONSE = LV_RESPONSE. "Append Receievd Response APPEND LS_CONSUMERESTSET TO ET_ENTITYSET. CLEAR LS_CONSUMERESTSET. endmethod.
- Save and activate this method.
[3] Testing OData-Service which consumes REST-Service
- Once above implementation gets completed, we can begin testing of service.
- Note: before testing make sure, odataService is already registered, for same you can refer below blog of mine:
- For testing in Fiori-server, go to t-code ‘/n/iwfnd/gw_client’ and below oDataService uri will be used to invoke REST service via oData.
- here we are referring EntitySet ‘ConsumeRESTSet’
- and in property ‘REQUEST’ we are passing JSON-Format input string which need to be send to REST-Service while calling
- This JSON-Input-String is REST-Service’s input message format.
-
/sap/opu/odata/sap/ZTEST_ODATA_SRV/ConsumeRESTSet?$filter=REQUEST eq '[{"taskId":286079},{"taskId":287571}]'
- select Protocol ‘HTTP’
- select HTTP Method ‘GET’, here please note we are passing input in uri
- Execute press ‘F8’ -> and in acknowledgement, we received REST-Service’s output in Property ‘RESPONSE’.
- for reference Here ‘REQUEST’ property has same data which we supplied as an input.
[4] Debugging OData-Service which consumes REST-Service
- Lets debug method ‘ConsumeRESTSet_GET_ENTITYSET’ to understand data flow.
- Set external breakpoints
- Execute/trigger oData Service from t-code ‘/n/iwfnd/gw_client’
- Debugger enters in method, see first we have extracted input from property ‘REQUEST’ of ODataService’s EntitySet ‘ConsumeRESTSet’ into variable LV_REQUEST
- Next debugger stops post calling REST-Service, where we can understand with help of variable data ‘LV_RESPONSE’ which has extracted output from REST-Service acknowledgement
- The REST-Service’s output present in variable ‘LV_RESPONSE’. is been mapped/populated to oData-Service’ EntitySet ‘ConsumeRESTSet’ with help of structure ‘ET_ENTITYSET’ as shown in below screen
- Thus we have understood how data is flowing from oDataService call to REST-Service call.
Hi Dilip,
Thanks for sharing the information, This blog was really helpful.
I am trying for the same through post method but whatever the request payload, we are giving is not being taken in er_entity in create_entity method of data provider class and response is 201 but not able to see the output.
Could you kindly guide me the difference which we have to follow for post method to work as expected.
Thanks in advance
Regards
Ragav
Hi Ragavendiran,
Here, in this blog, trick I have used, is like,
Thus, in summary, OData-Service supports GET-Method which internally calles REST-Service as POST-Method.
Hope, above clears all the doubt.
Thanks & Regards,
Dilip
Great stuff , Thank you in Advance.
I am getting message (CSRF token validation failed) in Lv_responseand unable to call post method.
Hi Gaurav,
Thanks for comments @blog.
About "CSRF token validation failed":
Thanks & Regards,
Dilip
Thank you Dilip.. 🙂
Thank you for this post. This was a great resource getting me started on making external calls.
I have a user-exit in ECC where I would like to send a message to Gateway and have Gateway make a REST post call to an external web service. The purpose of this is to notify the external service when an event happens in ECC like a BOM change.
Do you know of any examples similar to this or have any suggestions?
I've also read up on the publish subscribe framework that exists within Gateway, but wasn't sure if if that it a better approach than creating a service in SEGW.
Thanks in advance,
Tom
Dear TOM,
To send messages to external service (REST POST), you don't have to go via OdataService (Gateway).
If your SAP-ECC can access internet content, then in your user-exit, you directly consume REST service to send messages
else, go via middle-ware like SAP-PI/PO, here proxy-to-REST scenario can be built-up, your user-exit should call PI's proxy-class passing message which will be forwarded to external services by PI/PO interface.
Thanks & Regards,
Dilip
Thank you for your reply. I was able to call directly from ECC as well as through Gateway.
Would there be any advantage to making this call through Gateway or PI as opposed to direct from ECC i.e. monitoring, delivery, decoupling environments, etc.?
We have taken the approach of decoupling our non-SAP calls through PI in the past. However, we are moving towards a more micro services oriented architecture in integrating systems with our SAP environment (recently added Gateway to our landscape). I’m trying to understand when is it better to communication with non-SAP systems/REST services directly from ECC , PI, or Gateway. If you have any thoughts around this I would appreciate your insight.
Thanks again,
Tom
Dear TOM,
Yes there is advantage of using middleware (Like PI) to deal with Internet protocols, which are:
Thanks & Regards
Dilip