Wednesday, December 7, 2016

Consuming SAP OData services from AngularJS and / or Node.js

In this blog I will give some examples on how to consume SAP OData services (e.g. from SAP Netweaver Gateway or SAP Cloud For Customer) using AngularJS or Node.js. By now most of us SAP developers know how this works when we are developing an SAP Fiori application using ODataModel, but things change when you have to call SAP OData services from different clients or server side runtimes. Two examples of these are AngularJS (a popular, very powerful JavaScript client-side framework) and Node.js (a server side JavaScript framework). Especially handling the CSRF token that SAP uses for OData services can be tricky at times. This blog will show you some examples on how to do read and write/modify/delete requests using AngularJS and Node.js.

Why AngularJS?


I described this in my blog earlier this year here. SAPUI5 / Fiori might not always fit your customers needs when you are developing client applications on top of SAP/HCP/Etc. Using Angular (or any other modern JavaScript client side framework like ReactJS etc), you are way more flexible with the look and feel of your application. You are not 'forced' to use the Fiori look and feel and the existing components that come with it. At TechEd this year, SAPUI5 / Fiori were dominant as always, but I attended some sessions that mentioned using Angular as a front-end framework and spoke to some people as well about using Angular and other frameworks. It's worth investigating these because they can really help you when you need to meet your customers needs.

Why Node.js?


Recently, i'm not sure when (I just started playing with this feature since TechEd 2016 Barcelona). SAP HANA Cloud Platform started supporting Cloud Foundry. This basically means that SAP is supporting the industry standard for cloud applications and that Java is not the only programming language that you can use on HCP on the server side. Examples of other programming languages that you can use now are Ruby, Python and Node.js. I used Node.js running on a Rasperry PI earlier this year to update service tickets in SAP Cloud for Customer during a Hackathon event at Acorel, so it was an obvious choice for me to show you how to call SAP OData services.


Consuming SAP OData services from AngularJS



As said, SAPUI5 uses ODataModel to communicate with SAP OData services (jQuery's $ajax is always an alternative of course because jQuery is included in SAPUI5). AngularJS is a framework on it's own and has it's own service to communicate with services (the $http service). 


GET Example
--------------------------------------------------------------------------------------------------------
var url = "<<your gateway service base url>>";
            
$http({
method: 'GET',
url:url+"<<your entity set>>?$format=json",
headers: { 
'x-csrf-token': 'Fetch',
'Authorization': 'Basic <<base64 encoded username:pass>>'
}
}).then(function(response){
$scope.token = response.headers('x-csrf-token');
});
--------------------------------------------------------------------------------------------------------

To do an insert/update, the code becomes a bit more complicated. Since SAP uses the x-csrf-token http header for cross site request forgery protection, we first need to fetch the token using a get request (as shown above). Using the x-csrf-token from the response we can then send a POST/PUT/DELETE request to the OData service.

POST Example
--------------------------------------------------------------------------------------------------------
var url = "<<your gateway service base url>>";
var entity = {};

entity.attribute1 = "Hello";
entity.attribute2 = "World";

$http({
method: 'POST',
url:url+"<<your entity set>>",
data: JSON.stringify(entity),
headers: { 
'x-csrf-token': $scope.token,
'Authorization': 'Basic <<base64 encoded username:pass>>'
}
}).then(function(response){

});
--------------------------------------------------------------------------------------------------------


Consuming SAP OData services from Node.js



Node.js can consume HTTP services without any additional modules, but it's easier to use one that makes your life easier. I like the request module. Using request, calling services is really straightforward.

GET Example
--------------------------------------------------------------------------------------------------------
var http = require('http');
var express = require('express')
var request = require('request')
var app = express()
var url = "<<your gateway service base url>>";

app.get('/', function (req, res) {
    
    var csrfToken;
    request({
              url:url+"<<your entity set>>?$format=json",
              headers:{
                "Authorization":"Basic <<base64 encoded username:pass>>",
                "Content-Type":"application/json",
                "x-csrf-token":"Fetch"
              }
      
    }, function (error, response, body) {
      if (!error && response.statusCode == 200) {   
          csrfToken = response.headers['x-csrf-token'];
          console.log(csrfToken);
          res.json(body);
      }
    });
})

app.listen(process.env.PORT || 3000, process.env.IP || "0.0.0.0", function(){
  console.log("App started");
});
--------------------------------------------------------------------------------------------------------

Just like in AngularJS, when we do a POST from Node.js we have to fetch / provide the X-CSRF-Token. The example below does a get request first and does the POST request after that. Of course it is better to store the token somewhere globally (like in Angular) for a user (e.g. as a session variable), instead of doing a get every time before the insert / update operation. The example below is just a simple example to show you how you can Fetch the token en send the token in Node.js using the request module.

POST Example
--------------------------------------------------------------------------------------------------------
var http = require('http');
var express = require('express')
var request = require('request')
var app = express()
var url = "<<your gateway service base url>>";


app.get('/', function (req, res) {
    
    var csrfToken;
    
    request({
              url:url+"<<your entity set>>?$format=json", // or xml
              headers:{
                "Authorization":"Basic <<base64 encoded username:pass>>",
                "Content-Type":"application/json",
                "x-csrf-token":"Fetch" // get CSRF Token for post or update
              }
    }, function (error, response, body) {
      if (!error && response.statusCode == 200) {
          
          // Get token
          csrfToken = response.headers['x-csrf-token'];
          
          // New entity
          var entity = {};
          
          // Fill entity
          entity.attribute1 = "Hello";
          entity.attribute2 = "World";
          
          // Do post
          request({
                  url:url+"<<your entity set>>",
                  method: 'POST',
                  headers:{
                    "Authorization":"Basic <<base64 encoded username:pass>>",
                    "Content-Type":"application/json",
                    "x-csrf-token":csrfToken // set CSRF Token for post or update
                  },
                  json: entity
          }, function(error, response, body){
              
              // handle response
              
          });

      }
    });
    
})

app.listen(process.env.PORT || 3000, process.env.IP || "0.0.0.0", function(){
  console.log("App started");
});
--------------------------------------------------------------------------------------------------------

Conclusion

The examples above show how easy it is to consume SAP Netweaver Gateway services in AngularJS and Node.js and how to handle the x-csrf-token manually from other frameworks besides SAPUI5. Using the same approach it is possible to consume SAP Netweaver Gateway services in any other programming language.

1 comment: