cancel
Showing results for 
Search instead for 
Did you mean: 

JSONP and CORS Not Working for fixing access control allow origin issue

Former Member
0 Kudos

Hi Experts,
I am trying to access a RESTful Service( Created in SICF, Implementation written in Handler Class) from a JavaScript application.
Now, i am facing the issue of Cross Control Access Origin.
After hours of research, i found out that there are two ways to solve this:
a) JSONP
b) CORS

a) JSONP :
Now, i used JSONP with Callback function and it is calling the SAP REST Handler and setting the response in JSON( Used CL_FDT_JSON Class for converting ABAP to JSON). But, the AJAX call is going to error section giving "Parse Error" in the Browser Development tools.
I believe SAP is not compatible with JSONP and that's why i am not getting the success result(Please CORRECT ME IF I AM WRONG)


Here is the code,
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<p>Enter Ticket Number
<input id="Text1" type="text" /></p>
<p>
  Enter Mode
  
<input id="Text2" type="text" />
</p>
<input type='submit' onclick='submitForm2()' name='btn' value='Call SAP' />
<table id="myTable" style="width:100%;"></table>
<script>  

  function submitForm2() {
  $.ajax({
  url: "http://DOMAIN.com:8000/sap/bc/ztrees_rest_api?id=123&callback=?",
  dataType: "jsonp",
  success: function (json) {
  alert("Success");
  },
  error: function(e) {
  alert("The error is",e.message);
  }
  });

  }

  function jsonCallback(json) {
  alert(json);
  }


b) CORS
Now, the second option was to use CORS.
I added the header parameter while sending the response from the Handler Class
server->response->set_header_field( Exporting name = 'Access-Control-Allow-Origin'

                                                                                    value = '*' )

Now, the modified code for this part is

function submitForm2() {

  $.ajax({
  type: 'GET'

  url: "http://DOMAIN.com:8000/sap/bc/ztrees_rest_api?id=123",

  dataType: "json",

  success: function (json) {

  alert("Success");

  },

  error: function(e) {

  alert("The error is",e.message);

  }

  });

  }

In this case, the SAP Server is not even getting called and i am getting 401 Unauthorized and Access Control Allow Origin Error is appearing from the beginning.

Note:
I can disable the Web Security to make it work but it is for practical implementation.

Apologies for the long post but i am going mental and i am unable to fix it.

Thanks
Sahil

Accepted Solutions (1)

Accepted Solutions (1)

rdiger_plantiko2
Active Contributor
0 Kudos

Hi Sahil Shah,

the statement "ABAP does not support JSONP" does not make sense. ABAP supports whatever you want to have in the response payload. You can send any data in any form in your HTTP request handler - everything depends on your implementation of the HTTP request handler.

JSONP simply means that you have to program your request handler in such a way that it wraps its normal response '...' into a string 'handle( ... )', where 'handle' is an identifier name which is passed as request parameter to the service and has to be echoed as described into the response.

Your second issue, HTTP 401 Unauthorized, points to a different problem. Are you aware of the "preflight requests" with the OPTIONS verb which is sent ahead in advance instead of "GET" or "POST" and doesn't require Authentication Data (instead of the "real" GET request which is sent afterwards and which does require the Authentication Data)? See javascript - 401 when trying to implement CORS for SharePoint - Stack Overflow for an introduction into this general CORS issue.

Regards,

Rüdiger

Former Member
0 Kudos

Hi Former Member,
Thank you very much for taking the time to reply. You won't believe your post on Creating Rest Services in ABAP helped our team a lot and made us more efficient.

Regarding the issue, I got it fixed through CORS by sending xhr:withCredentials as true in my AJAX Request and adding Access-Control-Allow-Credentials as TRUE and Access-Control-Allow-Origin to the host that was calling in the  SICF Handler.

Although, talking about JSONP, The debugger hits the Handler and the response is written to the Server. But, The success function at the Client Side returns Null, and i am getting the response data in an extended URI : http:/XXXX:8000/sap/bc/zrest_demo?sap-client=200&callback=jQuery1620229378700197‌​5638_1466390509710&_=1466390513676


According to JSONP, the results should be returned in a Callback function but in case of SAP as a server, it is being stored in an intermediate function but not transferred completely to the request.

Any suggestions/examples on using JSONP with SAP would be awesome.

Thanks
Sourav

rdiger_plantiko2
Active Contributor
0 Kudos

Hi Sourav,

nice to hear that my blog was helpful for you!

So you found a workaround for the CORS way, OK I think the browser automatically sends a preflight request with OPTIONS as HTTP verb, basically for evaluating the Access-Control-Allow-Origin response, and this preflight request by default has to be served without authentication. But it seems you found a way to do this preflight step with authentication too.

As for JSONP: I still don't believe you. If you put an UTF8 bytestring into the HTTP response body with server->response->set_data( ... ), it will appear precisely in this form on the client. Please show me an example where this is not the case. The rest of JSONP only depends on whether your handler codes the convention correctly. The response string has to be valid JavaScript code of the form "handler( ... )", where the name handler is arbitrary and should be taken from a from parameter to decouple server from client (the freedom to change the name of the function is then up to the client, with no server change necessary), and the ... contains the actual parameters of this function call, usually in the form of JSON data.

Regards,

Rüdiger

Former Member
0 Kudos

HI Former Member,
You are right. My confusion was Valid JSON is the same thing as valid JSONP which is not. The reason i was not getting the Success Call is because i was sending a valid JSON structure instead of a JSONP Structure.
A valid JSONP Structure would look like callbackMethod( {JSON} ).
So, i manually wrapped up my server's JSON using the callBackMethod and it worked.

        $.ajax({

            type: "GET",

            url: url,

            dataType: "jsonp",

            jsonp: false,

            jsonpCallback: "myJsonMethod",

            success: function (data) {

                console.log(data);

            },

            error: function (httpReq, status, exception) {

                alert(status + " " + exception);

            }

        });


Here, i added myJsonMethod( ) in the Handler Class to the JSON Output to make it work.

Finally, the good news is we can do both CORS and JSON with SAP. That's Awesome!!!

Thanks
Sahil

rdiger_plantiko2
Active Contributor
0 Kudos

Hi Sahil,

thanks for the notification, this was what I expected!

Cheers

Rüdiger

Former Member
0 Kudos

Hi Rudiger, Former Member
Now, after so much of happy news i got a bad one, So, while doing CORS request, i am getting a Popup to enter user id and password for the SAP System and then it navigates to SAP.

Now, i want to suppress that popup and send the user id and password as part of the request, For which i used xhr.setRequestHeader("Authorization", "Basic" + btoa("Username:Password")
which is not working and i am getting a CORS error again.
This is due to the fact that in case of CORS, browser sends an Preflight request and sends an OPTIONS request instead or a GET. Since the web service wants authentication data and doesn’t see any, it returns a 401 error to the Browser.


Now, normally people disable authentication on OPTIONS request by configuring their servers.
But in case of SAP, i am completely clueless how to configure or fix this solution.
Kindly, help if you have tried this way and it worked for you.


Note: This works in IE but not in Chrome and Firefox.

( I am being too ambitious to tag Thomas here, but this is a legitimate issue and there is no solut...


Thanks

Sahil

rdiger_plantiko2
Active Contributor
0 Kudos

Hi Sahil,

it sounds strange for me to differentiate authentication depending on the verb of the HTTP request - you want no authentication if the verb is OPTIONS, while requiring valid login when the verb is GET (say).

In my eyes, the straightforward approach would be to do the authentication in advance in the surrounding (the site where you send your CORS requests to the SAP system).

If you want to keep your design, I only see the way to make a separate handler for your CORS requests. This separate handler has an anonymous login (meaning that login data are defined for it in SICF), and a self-implemented handler class ZCL_CORS_REDIRECT which

  • sends the required header fields as reply to OPTIONS request
  • sends a redirect HTTP 301 to the "real" service URL in case of a GET request.
  • This "real" service URL then requires login (i.e. no login data specified for the service in SICF)

Client-side frameworks like jQuery automatically handle redirects in Ajax, retrieving the data that finally are sent by the last URL they are redirected to.

Regards,

Rüdiger

Former Member
0 Kudos

Hi Former Member,
Rudiger, Thanks for your prompt reply. Your idea makes most sense. I tried your way and created a redirect handler and set the login for the Web Service.

Now,  the SAP System is called 2 times:
a) First it is called as an Preflight OPTIONS request with Authorization in the Request Headers where i am setting all the headers in the response.
b) Secondly, it is called as an Normal GET Request.

Now, when you talk about Reditect HTTP 301, Are you referring to SICF Redirection, Web Dispatcher Redirection or through ABAP. Can you kindly clarify?

Secondly, how can i pass the authorization details i got in the First request to the redirected URL.

Thanks
Sahil

rdiger_plantiko2
Active Contributor
0 Kudos

Hi Sahil,

I thought of server->response->redirect(  ). In the GET handler part of ZCL_CORS_REDIRECT, determine the "real" target URL from the request (you could use parts of the path coming behind the node to which the ZCL_CORS_REDIRECT handler is assigned, or by a URL query parameter. This information could be considered as a kind of code for determining the "real" target URL). Then call server->response->redirect( ... ) to address the client to do the redirect.

There may be other ways to do it, but this one should work. As I said, the point is, that clientside Ajax requests usually are able to handle a HTTP 301 redirect internally. Actually, they have to implement redirection as the W3C XMLHTTPRequest spec prescribes.

Regards,

Rüdiger

rdiger_plantiko2
Active Contributor

I just performed a little test, and unfortunately my proposed method doesn't work. The problem is that even the redirect requires the preflight request, so the first OPTIONS request will now be served, but after the redirect we are again at the OPTIONS request of the second URL which does require authentication but is supposed to be anonymous by the CORS specification. Which is the original problem. As far as I see, there is no way to switch off the OPTIONS request.

Therefore I must give up. I see no way to bring this to work. The main point why this is not possible in SICF is that authentication is performed before the ABAP stack is created and before the HTTP_DISPATCH_REQUEST and the request handler is invoked.

Sorry for having put you on the worng track with my last answer... 😞

Former Member
0 Kudos

Hi Former Member,
Thanks for the effort. Much Appreciated.
As a backup plan, i am fetching the credentials in a POST requested in Base64 Encoded Format.
I am decoding it and calling SUSER_CHECK_LOGIN FM to validate and manually set the status code for success and failure.

If you get the solution to this in the future, kindly let me know.
Once again, thanks.

Thanks
Sourav

rdiger_plantiko2
Active Contributor

"Authenticated CORS" should be addressed to the authors of SAP's REST framework. The problem how the OPTIONS request can be handled without authorization, while all the other requests should perform the authorization checks as defined for that SICF node, is not handled.

Former Member
0 Kudos

You are right. In the rest of the places in SCN, they have taken the route of implementing Reverse Proxy.

Now, i have to ask the Basis Team to set up the Reverse Proxy and see if things get better. Do you have any docs related to Reverse Proxy.


Thanks
Sahil

Answers (0)