Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
alespad
Contributor

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:

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

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.

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