Skip to Content

Cross-domain communications with ABAP and JSONP

In this blog i want to create an ABAP Custom HTTP handler using JSONP, a  pattern of usage that allows a page to request data from a server in a  different domain.

As a solution to this problem, JSONP is an alternative  to a more recent method called Cross Origin Resource Sharing ( http://en.wikipedia.org/wiki/JSONP ).   Our ABAP Http service will generate Javascript code that will call the callback function and pass in a JSON containing Names of Abap Classes  (a kind of SE24 Matchcode)

RESTful  services and json are a great way to get Sap data into a website like an Intranet Homepage or any other external websites.

However,we have to consider that Under the same origin policy there is no way to make a request to a different domain from JavaScript using AJAX because of security restrictions.   I really liked the blog “Deliver dynamic search of SAP data into a website using RESTful services” by Sap Mentor John Moy  (Deliver dynamic search of SAP data into a website using RESTful services) .

Especially , I was very impressed about CORS (Cross Origin Resource Sharing,follow this good link from post by John to learn about CORS ( http://bit.ly/9iT2Na ).

CORS allows method GET and POST , it uses regular XMLHttpRequest but it’snt supported by all browser (Firfox 3.5+,Safari 4+,Chrome,partially in IE8).   Another way to request data from a server in a different domain is JSONP. JSONP  is a  script element injection into the DOM , so our Abap Service Url will be included dinamically between using Javascript.  What JSONP does is add a  element to the DOM, with the external URL (in this case,an Abap Service URL) as the SRC target.

However, i think that JSONP is a temporary solution until full support for CORS that  will be the definitely way. Below the example: image

Part 1 : HTML / Javascript

function showAbapClasses(jsonAbap){
var htmlString = "<br/><div>Abap Response:</div>";
 for (var i = 0; i < jsonAbap.classes.length; i++) {
                htmlString += "<br/>" + jsonAbap.classes[i];
            }
               document.getElementById("results").innerHTML = htmlString;
    } 

The Abap response will be :

showAbapClasses({classes:["CL_ABAP_GZIP","CL_ABAP_GZIP_BINARY_STREAM",
"CL_ABAP_GZIP_TEXT_STREAM","CL_ABAP_UNGZIP_BINARY_STREAM",
"CL_ABAP_UNGZIP_TEXT_STREAM","CL_ABAP_ZIP"]});

The javascript code injection into the DOM:

var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("SCRIPT");
    script.type = "text/javascript";
    script.src = "http://sapfqdn:8000/zjsonp?q=" +
                   encodeURIComponent(document.getElementById("input_field").value)
                  + "&callback=showAbapClasses";
    script.id = "JSONP";
    head.appendChild(script);

Our script element dynamically Inserted into the DOM (our Abap SICF service) will be: 

http://sapfqdn:8000/zjsonp?q=CL*ABAP*ZIP&callback=showAbapClasses

/wp-content/uploads/2011/05/jsonp1_81496.jpg

/wp-content/uploads/2011/05/jsonp2_81500.jpg

Javascript complete code

//12.05.2011
//Alessandro Spadoni
//this is just demonstration code that i'm using to illustrate abap & jsonp
function showAbapClasses(jsonAbap){
var htmlString = "<br/><div>Abap Response:</div>";
 for (var i = 0; i < jsonAbap.classes.length; i++) {
                htmlString += "<br/>" + jsonAbap.classes[i];
            }
               document.getElementById("results").innerHTML = htmlString;
    }    
function abap_jsonp() {
if (document.getElementById("input_field").value){
 // Remove any old script tags.
 var script;
  while (script = document.getElementById("JSONP")) {
    script.parentNode.removeChild(script);
    // Browsers won't garbage collect this object.
    // So castrate it to avoid a major memory leak.
    for (var prop in script) {
      delete script[prop];
    }
  }
    var head = document.getElementsByTagName("head")[0];
    var script = doPcument.createElement("SCRIPT");
    script.type = "text/javascript";
    script.src = "http://sapfqdn:8000/sap/zjsonp?q=" + encodeURIComponent(document.getElementById("input_field").value)
                  + "&callback=showAbapClasses";
    script.id = "JSONP";
    head.appendChild(script);    
} 

Part 2 : ABAP

The Abap HTTP Handler will generate JAVASCRIPT concatenating the callback function and the JSON object. In this example I created Json using a simple concatenate statement.

/wp-content/uploads/2011/05/jsonp4_81501.jpg

/wp-content/uploads/2011/05/jsonp3_81502.jpg

method IF_HTTP_EXTENSION~HANDLE_REQUEST.
**12.05.2011
**Alessandro Spadoni
**this is just demonstration code that i'm using to illustrate abap & jsonp
  DATA: method          TYPE string,
  query_string  TYPE string,
  callback      TYPE string,
  json_response TYPE string,
  t_classname TYPE TABLE OF seoclsname,
  r_classname TYPE RANGE OF seoclsname,
  l_classname LIKE LINE OF r_classname,
  l_lines LIKE sy-tfill.
  FIELD-SYMBOLS: <classname> TYPE seoclsname.
  method = server->request->get_header_field( name = '~request_method' ).
  query_string = server->request->GET_FORM_FIELD_CS( name = 'q' ).
  callback = server->request->GET_FORM_FIELD_CS( name = 'callback' ).
**method not allowed
  IF method <> 'GET'.
    server->response->set_status( code = '405' reason = 'You must use GET' ).
    EXIT.
  ENDIF.
**bad request
  IF query_string IS INITIAL.
    server->response->set_status( code = '400' reason = 'Query String is initial' ).
    EXIT.
  ENDIF.
  IF callback IS INITIAL.
    server->response->set_status( code = '400' reason = 'Callback is initial' ).
    EXIT.
  ENDIF.
**fill range
  l_classname-sign = 'I'.
  l_classname-low = query_string.
  IF query_string CS '*'.
    l_classname-option = 'CP'.
  ELSE.
    l_classname-option = 'EQ'.
  ENDIF.
  APPEND l_classname TO r_classname.
**example -> max 25 rows
  SELECT clsname INTO TABLE t_classname UP TO 25 ROWS FROM seoclass
                                             WHERE clsname IN r_classname.
  DESCRIBE TABLE t_classname LINES l_lines.
  CONCATENATE callback '({classes:[' INTO json_response.
  LOOP AT t_classname ASSIGNING <classname>.
    CONCATENATE json_response '"' <classname> '"' INTO json_response.
    IF sy-tabix < l_lines.
      CONCATENATE json_response ',' INTO json_response.
    ENDIF.
  ENDLOOP.
  CONCATENATE json_response ']});' INTO json_response.
  server->response->set_status( code = '200' reason = 'OK' ).
  server->response->set_header_field( name = 'Content-Type' value = 'application/javascript' ).
  server->response->set_cdata( data = json_response ).
endmethod.
3 Comments
You must be Logged on to comment or reply to a post.
  • Hi Alessandro,

    well done with the blog demonstrating how customers can do 'cool' AJAXy webpages and integration right now, in their existing systems, and without additional software!

    I also like the flow diagram - it really helps explain what's going on here.

    Thank you for sharing!

    Sascha

  • Hi Alessandro,

    Great blog, and thanks for the mention.  I did investigate JSONP, but didn't get to around to implementing it so I appreciate your thoroughly documented example. 

    I should mention that my own investigations into CORS came about because of a comment by Chris Paine about it as a response to one of my earlier blogs.  Now you've covered off the JSONP approach.  I guess that's the community at work!

    Well done.

    John