Skip to Content
Author's profile photo Edmund Häfele

Post data from Node-RED to SAP Gateway (OData)

SAP Gateway allows to easily expose services of a SAP backend system: Those services then can be consumed by “modern” types of web applications. For a demo case, I had the requirement of bringing in some new data from a web application into a SAP backend system for further processing.

I implemented that via a Node-RED flow: Node-RED is a programming tool for wiring together devices, APIs and online services in a graphical manner. Such a “flow” is built in via a browser-based editor and then can be deployed to the Node-RED runtime running either on-premise or in the cloud.

In this blog, I’ll show how to handle “Create” (HTTP POST) operations via SAP Gateway for such a Node-RED flow: It took me some iterations to get this running – so I thought it is worth to share this experiences… (I don’t focus on service creation here – and assume that the “Create” service is already defined in the SAP Gateway)

Prerequisites: SAP Gateway knowledge, and a rudimentary knowledge of Node-RED, Javascript and http requests.

Background

SAP Blog “Gateway protection against Cross-Site Request Forgery attacks” explains in detail the protection mechanisms of SAP Gateway against Cross-Site Request Forgery (CSRF): A client application, which likes to invoke a transaction via a modifying request (HTTP POST, PUT or DELETE) via an SAP Gateway REST service, needs to authenticate first and the request needs to be signed with a CSRF-Token as a secret key. The CSRF token is only known by the client application context and the SAP Gateway.

To request the CSRF-Token the client application issues a GET request with header variable X-CSRF-Token set to value ‘fetch’ to the SAP Gateway service first – the SAP Gateway service then generates a new random token and returns the value in the response (via header variable and cookie). The client application reads the CSRF Token from the HTTP GET Response, and includes the value as header parameter X-CSRF-Token for the modifying (HTTP POST, PUT DELETE) request to the SAP Gateway. As the token value is also part of the response via cookie, the cookie needs to be included in the modifying request.
(The SAP Internet Communication Framework (ICF) runtime on the SAP Gateway system checks for the X-CSRF-Token in both request header and cookie – the token needs to be present in both, and the values need to be equal – only then the transaction is executed via the SAP Gateway).

Modelling in a Node-RED flow

The flow below is an example for handling the x-crsf-token and session cookie to avoid HTTP error 403 (Forbidden) in the POST request within Node-RED. The example flow is manually triggered, the X-CSRF-Token fetch request is added to the http header, an first GET request to the SAP Gateway fetches the token, then both token and cookie are added to the (new) header, post data for the transaction is added to the payload, and then the POST call gets issued.

Here a detailed view on the respective nodes of the flow:

Function Node to prepare the GET request

The function node has the appropriate JavaScript code for the token request in the header:

// Request CSRF token with fetch
msg.headers = {};
msg.headers["accept"] = "application/json";
msg.headers["x-csrf-token"] = 'fetch';
return msg;

HTTP Request Node (GET to SAP Gateway)

The HTTP header is submitted via GET to the SAP Gateway service:

As a result, SAP Gateway returns the CSRF token and the session cookie – those are extracted in the next node.

Function Node to prepare the POST request

CSRF and cookie of the response are extracted and added to the header for the next request. pData contains the object which is sent via POST to the SAP Gateway service – this needs to correspond to the data format expected in the service description:

var token = msg.headers["x-csrf-token"];
var cookie = msg.headers["set-cookie"];
// msg.header
msg.headers = {};
msg.headers["cookie"] = cookie;
msg.headers["x-csrf-token"] = token;
msg.headers["content-type"] = 'application/json';
msg.headers["charset"] = 'utf8';
// msg.payload = "data to post"
var pData = {"d":{"Barcode":"00000098","Name":"TEST ENTRY 98"}};
msg.payload = pData; 
return msg;

HTTP Request Node (POST to SAP Gateway)

Final POST submitting the header and the transaction data to the SAP Gateway service

Sample flow to import into Node-RED

Import the flow and modify << SAP Gateway Service URI >>,
add user credentials, and modify the pData array corresponding to your SAP Gateway service accordingly:

[{"id":"f4ece51b.898f1","type":"tab","label":"SAP Gateway POST"},{"id":"27c09c2b.fce3bc","type":"debug","z":"f4ece51b.898f1","name":"Result final","active":true,"console":"false","complete":"true","x":1059.9722900390625,"y":258.4444580078125,"wires":[]},{"id":"86d117c4.bdb0b8","type":"http request","z":"f4ece51b.898f1","name":"Call POST to SAP Gateway","method":"POST","ret":"txt","url":"<< SAP Gateway Service URI >>","tls":"","x":827.083251953125,"y":258.5833435058594,"wires":[["27c09c2b.fce3bc"]]},{"id":"476828e7.c82c3","type":"function","z":"f4ece51b.898f1","name":"Set token and cookie in msg.headers","func":"var token = msg.headers[\"x-csrf-token\"];\nvar cookie = msg.headers[\"set-cookie\"];\n// msg.header\nmsg.headers = {};\nmsg.headers[\"cookie\"] = cookie;\nmsg.headers[\"x-csrf-token\"] = token;\nmsg.headers[\"content-type\"] = 'application/json';\nmsg.headers[\"charset\"] = 'utf8';\n// msg.payload = \"data to post\"\nvar pData = {\"d\":{\"Barcode\":\"00000098\",\"Name\":\"TEST ENTRY 98\"}};\nmsg.payload = pData; \nreturn msg;","outputs":1,"noerr":0,"x":499.0001220703125,"y":260.08349609375,"wires":[["86d117c4.bdb0b8"]]},{"id":"ef80a5c6.d40b18","type":"http request","z":"f4ece51b.898f1","name":"Call GET to SAP Gateway","method":"GET","ret":"obj","url":"<< SAP Gateway Service URI >>","tls":"","x":670.0833129882812,"y":105.58336639404297,"wires":[["476828e7.c82c3"]]},{"id":"1593bfe0.85f49","type":"function","z":"f4ece51b.898f1","name":"Set header to request CSRF token","func":"// Request CSRF token with fetch\nmsg.headers = {};\nmsg.headers[\"accept\"] = \"application/json\";\nmsg.headers[\"x-csrf-token\"] = 'fetch';\nreturn msg;","outputs":1,"noerr":0,"x":369.5,"y":105.49999237060547,"wires":[["ef80a5c6.d40b18"]]},{"id":"38e1cb45.423aac","type":"inject","z":"f4ece51b.898f1","name":"Trigger Flow","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":137.4444580078125,"y":42.72221374511719,"wires":[["1593bfe0.85f49"]]}]

 

So I hope this will help you connecting your Node-RED flows to SAP Gateway –
I will attempt to share my further experiences in further posts ;-

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Gayathri Ramachandran
      Gayathri Ramachandran

      Hello Edmund,

      Thanks for this great blog. I tried a similar one. After the POST, i get an error in my debug as this in the payload.

      The Data Services Request contains SystemQueryOptions that are not allowed for this Request Type

      Any idea

      Thanks

      Gayathri.R

      Author's profile photo Edmund Häfele
      Edmund Häfele
      Blog Post Author

      Hi Gayathri,

      there is one other blog where Ralf Handl from SAP replied to a similar question:

      https://answers.sap.com/questions/554921/the-data-services-request-contains-systemqueryopti.html

      Does that help? How do you call your service?

      Best regards

      Edmund

       

      Author's profile photo Sören Schlegel
      Sören Schlegel

      Hi Edmund,

       

      Thanks for the great blog! I have now invested several hours to recreate the example for a normal REST call and found a very nasty bug in your example when calling the POST:

       

      msg.headers["cookie"] = cookie;

       

      needs to be

       

      msg.headers["Cookie"] = cookie;

       

      with a Capital "C"

       

      Just documented here as help for future generations 🙂

       

      Regards

      Sören

      Author's profile photo Edmund Häfele
      Edmund Häfele
      Blog Post Author

      Thanks for sharing the hint!