Cross-domain AJAX request not working

40,551

Solution 1

The solution that I came up with was to use cURL (as @waki mentioned), but a slightly modified version that supports SOAP. Then, instead of making the AJAX call to the third party API (which is configured incorrectly) I make the call to my local PHP file which then makes a SOAP call to third party API and passes the data back to my PHP file where I can then process it. This allows me to forget about CORS and all of the complexities associated with it. Here's the code (taken and modified from this question, but without the authentication).

$post_data = "Some xml here";
$soapUrl = "http://yoursite.com/soap.asmx"; // asmx URL of WSDL


$headers = array(
    "Content-type: text/xml;charset=\"utf-8\"",
    "Accept: text/xml",
    "Cache-Control: no-cache",
    "Pragma: no-cache",
    "SOAPAction: http://yoursite.com/SOAPAction",
    "Content-length: " . strlen($post_data),
); //SOAPAction: your op URL

$url = $soapUrl;

// PHP cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); // the SOAP request
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$response = curl_exec($ch);

/* Check for an error when processing the request. */
if(curl_errno($ch) != 0) {
   // TODO handle the error
}

curl_close($ch);

// TODO Parse and process the $response variable (returned as XML)

Solution 2

To your first question,

What can I do to make this cross-domain request?

The following request headers are first sent to the server. Based on the response headers, the UserAgent, i.e. browser here, will discard the response(if any) and not give it back to the XHR callback, when the headers don't add up.

Origin: http://yourdomain.com Access-Control-Request-Method: POST Access-Control-Request-Headers: X-Custom-Header

Your server should then respond with the following headers:

Access-Control-Allow-Origin: http://yourdomain.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: X-Custom-Header

Example taken from - https://stackoverflow.com/a/8689332/1304559

You can use tools like Fiddler or Web Inspector Network tab(Chrome) or Firebug's network tab to find the headers the server is sending back in response to your request.

The third party API server url should respond back with the supported values. If it doesn't that's your problem.

Can't be of much help in JsonP 500 server error, as it says Internal Server Error


UPDATE

I saw your screen shots, couple of things to notice here

  1. HTTP POST is used
  2. No Access-Control headers found in both modes response. With second status code as 302
  3. Origin request header is null, so I believe the HTML file is opened from the file system rather than from a web site

Enabling JSONP

Coming to the point of why JSONP is not working, reason - The web service config(of the ASMX specified), has not enabled GET mode for the request. JSONP doesn't work with POST.

Why 200 with security disabled?

The POST request is directly called with no OPTIONS or preflight request fired before it, so server simply responds back.

Why 302 status code with security enabled?

First a little on how it happens when security flag is not disabled(default). The OPTIONS request is fired to the URL. The URL should respond back with a list of HTTP methods that can be used, i.e. GET, POST, etc. The status code of this OPTIONS request should be 200.

In the current case, there is a redirect called when doing this. And the redirect is pointing to a custom error handler for a HTTP Server Error. I believe it is 404 error, which is being caught and redirected.[This could be because the custom handler is set to ResponseRedirect instead of ResponseRewrite] The reason for the 404 is cross-domain access is not enabled.

In case the custom error handling was turned off, this would have returned HTTP 500 code. I believe the problem can be found by checking the server logs immediately after firing a cross-domain request. It will be good to check the server logs for errors other than this, if any.

Possible solution

To enable access, there are two ways - detailed here. Or directly add the access control headers to web.config file's customheaders section.

When cross-domain access is enabled, the server should respond back to OPTIONS and allow the request to go through. This should return HTTP 200 unless there are other errors. But ajax callback will not be able to access the response. This can be done now only if Access-Control-Allow-Origin is set to "*" or the Origin request header value, and the Access-Control as needed. And the ajax callback will receive the response as intended.

The third point, null Origin. This will be a problem if the Origin request header value is sent back as Access-Control-Allow-Origin. But I am assuming you will be moving your HTML page to a web server, so that shouldn't be a problem.

Hope this helps!

Solution 3

You can use cURL ? With Ajax you send data to your handler (on your server, let called callback.php). In your file (callback.php) use cURL with your data:

$post_data = array( 
    'key' => 'key', 
    'test' => 'text'
    ); 

$curl = curl_init(); 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); 
curl_setopt($curl, CURLOPT_POST, TRUE); 
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);  
curl_setopt($curl, CURLOPT_URL, 'http://www.domain.com/'); 
$return = json_decode(trim(curl_exec($curl)), TRUE); 
curl_close($curl); 

in your $return variable you get your data.

Share:
40,551
Don
Author by

Don

I help teams create more enjoyable environments in which to do their work.

Updated on July 09, 2022

Comments

  • Don
    Don almost 2 years

    I'm calling POST on a third-party API that I've been working with via jQuery's $.ajax function. However, when I make the call I get the following error: XMLHttpRequest cannot load http://the-url.com. The request was redirected to 'http://the-url.com/anotherlocation', which is disallowed for cross-origin requests that require preflight.

    I saw from this post that this might be a Webkit bug, so I tried it in Firefox (I'm developing in Chrome) and I got the same result.I've tried this on Chrome and Firefox and I get the same result.

    Per this post, I also tried using jsonp both by setting the crossDomain property of the $.ajax function to true and setting the dataType to jsonp. But, this caused a 500 internal server error.

    When I start Chrome with the --disable-web-security flag, I don't have any problems. However, if I start the browser normally, then I get the error.

    So, I guess this might sort of be a 2-part question. What can I do to make this cross-domain request? If JSONP is the answer, then how do I go about figuring out if the third-party API is set up correctly to support this?

    EDIT: Here's the screenshot when I make the call with the browser security disabled: https://drive.google.com/file/d/0Bzo7loNBQcmjUjk5YWNWLXM2SVE/edit?usp=sharing

    Here's the screenchost when I make the call with the browser security enabled (like normal): https://drive.google.com/file/d/0Bzo7loNBQcmjam5NQ3BKWUluRE0/edit?usp=sharing

  • Don
    Don about 10 years
    Thank you. This was helpful. I looked at the network calls in the Network tab on Web Inspector and I didn't see the necessary Access-Control headers in there. I'm thinking that might be the problem. If the headers were there, they'd show up in the Network tab on the Web Inspector, correct?
  • aravind
    aravind about 10 years
    Yup, that's correct. You can try it out on the Network tab for a script downloading from a CDN in your HTML page, eg: ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js Similarly the third party API should enable the Access-Control headers as needed.
  • Don
    Don about 10 years
    Okay. I see what your talking about and I'm thinking about contacting the provider of the API to see why they don't support JSONP. I'm attaching a screenshot of the network tab to the question. Will you please take a look and let me know what you think?
  • aravind
    aravind about 10 years
    Sure thing! That'll help in understanding the problem better
  • aravind
    aravind about 10 years
    Updated my answer, let me know if it helps pin down the issue
  • aravind
    aravind about 10 years
    @Don Did you get to try out my suggestion? Does it help?
  • Don
    Don about 10 years
    I looked over your answer, but unfortunately, I don't have access to the server. So, the solution that I've come up with is similar to what @waki posted, but a slightly modified to support SOAP instead. Thanks again for your help!
  • Don
    Don about 10 years
    Great suggestion, thank you! I actually went with a slightly modified version of this that supports SOAP.
  • aravind
    aravind about 10 years
    Great! Glad to hear you fixed your problem. Proxy-ing requests. Not bad a solution at all. Glad to have been of help.
  • Yann
    Yann over 8 years
    That would work, but I would call this a "workaround" rather than a solution. Some people can only rely on front-end in some cases, and don't have the option to use a back-end proxy.
  • Don
    Don over 8 years
    Yes, I wish there was a better solution. However, due to the restrictions imposed by security and not having control over the server, proxying it through a server that I control is the best solution that I've come across.