Skip to Content

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.
To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. 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

    (0) 
  2. 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

    (0) 
  3. lisa debora

    Hi Alessandro,

    Awesome blog, and thanks for sharing…!!

    The three basic objects for defining data in the ABAP Dictionary are Domains, Data elements and Tables. The domain is used for the technical definition of a table field such as field type and length, and the data element is used for the semantic definition (short description). A data element describes the meaning of a domain in a certain business context. It contains primarily the field help and the field labels in the screen.

    (0) 

Leave a Reply