Skip to Content
Author's profile photo Alessandro Spadoni

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.

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sascha Wenninger
      Sascha Wenninger
      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

      Author's profile photo Alessandro Spadoni
      Alessandro Spadoni
      Blog Post Author
      Hi Sascha,
      i'm glad you appreciated!(just found you on twitter)

      Alessandro

      Author's profile photo John Moy
      John Moy
      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