24

I need to make an AJAX request from a website to a REST web service hosted in another domain.

Although this is works just fine in Internet Explorer, other browsers such as Mozilla and Google Chrome impose far stricter security restrictions, which prohibit cross-site AJAX requests.

The problem is that I have no control over the domain nor the web server where the site is hosted. This means that my REST web service must run somewhere else, and I can't put in place any redirection mechanism.

Here is the JavaScript code that makes the asynchronous call:

var serviceUrl = "http://myservicedomain";
var payload = "<myRequest><content>Some content</content></myRequest>";
var request = new XMLHttpRequest();
request.open("POST", serviceUrl, true); // <-- This fails in Mozilla Firefox amongst other browsers
request.setRequestHeader("Content-type", "text/xml");
request.send(payload);

How can I have this work in other browsers beside Internet Explorer?

Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154

8 Answers8

14

maybe JSONP can help.

NB youll have to change your messages to use json instead of xml

Edit

Major sites such as flickr and twitter support jsonp with callbacks etc

reevesy
  • 3,452
  • 1
  • 26
  • 23
redsquare
  • 78,161
  • 20
  • 151
  • 159
  • this might work, but JSONP is an admission that something's wrong :) – annakata Dec 02 '08 at 10:24
  • how popular is this approach? It seems kind of experimental. – Enrico Campidoglio Dec 02 '08 at 10:37
  • @annakata obviously but he metnioned he has no handle over the web server etc so using a proxy is out of the window. I'm not here to comment on his situation just provide a possible solution to his question/problem. – redsquare Dec 02 '08 at 10:50
  • 1
    @redsquare: agreed, he's in a corner @enrico: quite experimental, but it is used – annakata Dec 02 '08 at 11:13
  • Even though posting the AJAX request from an iframe hosted on the same domain as the REST service is a viable solution (as long as there is no need for communication between the iframe and the page), I'm not very fond of iframes in general because of their compatibility issues. So JSONNP is probably the best solution currently available. – Enrico Campidoglio Jul 08 '09 at 08:29
  • but from my understanding, jsonp requires the server embed a call to the "callback" function passed in the jsonp request, so this wouldn't work in this situation, since he doesn't have control over the server... read the following for a nice overview of jsonp: http://www.insideria.com/2009/03/what-in-the-heck-is-jsonp-and.html – Brad Parks Jan 08 '10 at 17:24
  • No reason you couldn't use the same approach fro "`XMLP`". – Michael Mior Jul 28 '11 at 13:38
5

The post marked as the answer is erroneous: the iframes document is NOT able to access the parent. The same origin policy works both ways.

The fact is that it is not possible in any way to consume a rest based webservice using xmlhttprequest. The only way to load data from a different domain (without any framework) is to use JSONP. Any other solutions demand a serverside proxy located on your own domain, or a client side proxy located on the remote domain and som sort of cross-site communication (like easyXDM) to communicate between the documents.

Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
Sean Kinsey
  • 37,689
  • 7
  • 52
  • 71
  • -1 because this isn't inaccurate. It is possible to access the parent page from an IFRAME with JavaScript by using the 'parent' DOM object. For example this will modify an element in the page from within an IFRAME: parent.document.getElementById("someDiv").innerHTML = "some content"; – Enrico Campidoglio Jul 07 '09 at 12:14
  • 2
    The question is about document from different domain - and the same origin policy will therefor be i effect. An IFrame from domainA is NOT able to access the contentDocument of its parent if the parent is from domainB. As the author of the easyXSS cross-site library, I do have some knowledge around this. – Sean Kinsey Jul 07 '09 at 21:58
  • I missunderstood your answer then. You are right, an iframe cannot access the parent page's content and viceversa when the two are being hosted in different domains. However in my case using an iframe works well, because it doesn't need to access the parent page in any way, but it simply posts an AJAX request to a service, which is hosted on its same domain. – Enrico Campidoglio Jul 08 '09 at 08:11
  • You need to edit your answer, since SO won't allow me to change a vote that's too old – Enrico Campidoglio Jul 25 '09 at 12:18
  • easyXSS has been renamed to easyXDM, and it has also been improved quite a bit – Sean Kinsey Jan 13 '10 at 11:13
3

The fact that this works in IE is a security issue with IE, not a feature.

Unfortunately cross-site scripting is prohibited, and the accepted work around is to proxy the requests through your own domain: do you really have no ability to add or modify server side code?

Furthermore, the secondary workaround - involving the aquisition of data through script tags - is only going to support GET requests, which you might be able to hack with a SOAP service, but not so much with the POST request to a RESTful service you describe.

I'm really not sure an AJAX solution exists, you might be back to a <form> solution.

annakata
  • 74,572
  • 17
  • 113
  • 180
  • I agree that allowing XSS is a security flaw in IE. Unfortunately, I don't have any control over the web server, since it is a blog hosted on a third-party provider. I also thought about using HTML forms, but that would cause a full page post instead of a nice asynchronous call. – Enrico Campidoglio Dec 02 '08 at 10:36
3

The not very clear workaround (but works) is using iframe as container for requests to another sites. The problem is, the parent can not access iframe's content, can only navigate iframe's "src" attribut. But the iframe content can access parent's content.

So, if the iframe's content know, they can call some javascript content in parent page or directly access parent's DOM.

EDIT: Sample:

function ajaxWorkaroung() {
    var frm = gewtElementById("myIFrame")
    frm.src = "http://some_other_domain"
}
function ajaxCallback(parameter){
    // this function will be called from myIFrame's content
}
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • 2
    IFrames are slower than ajax, and also in older IE I remember there is a 'click' sound played every iframe.src update :( – Thinker Jul 05 '09 at 21:42
  • In javascript one can not access the parent javascript data or functions cros-domain. The answer is untrue. – naugtur Jun 23 '10 at 17:17
  • @nagtur: I successfully used this scenario in some time ago :|. – TcKs Jun 24 '10 at 08:57
2

Make your service domain accept cross origin resource sharing (CORS).

Typical scenario: Most CORS compliant browsers will first send an OPTIONS header, to which, the server should return information about which headers are accepted. If the headers satisfy the service's requirements for the request provided (Allowed Methods being GET and POST, Allowed-Origin *, etc), the browser will then resend the request with the appropriate method (GET, POST, etc.).

Everything this point forward is the same as when you are using IE, or more simply, if you were posting to the same domain.

Caviots: Some service development SDK's (WCF in particular) will attempt to process the request, in which case you need to preprocess the OPTIONS Method to respond to the request and avoid the method being called twice on the server.

In short, the problem lies server-side.

Edit There is one issue with IE 9 and below with CORS, in that it is not fully implemented. Luckily, you can solve this problem by making your calls from server-side code to the service and have it come back through your server (e.g. mypage.aspx?service=blah&method=blahblah&p0=firstParam=something). From here, your server side code should implement a request/response stream model.

jkrieg
  • 116
  • 4
1

Just use a server side proxy on your origin domain. Here is an example: http://jquery-howto.blogspot.com/2009/04/cross-domain-ajax-querying-with-jquery.html

Laurent
  • 1,048
  • 11
  • 23
0

This can also be done using a webserver setup localy that calls curl with the correct arguments and returns the curl output.

app.rb

require 'sinatra'
require 'curb'

set :views,lambda {"views/"+self.name.to_s.downcase.sub("controller","")}
set :haml, :layout => :'../layout', :format => :html5, :escape_html=>true
disable :raise_errors

get '/data/:brand' do
  data_link =  "https://externalsite.com/#{params[:brand]}"
  c = Curl::Easy.perform(data_link)
  c.body_str
end

Sending an ajax request to localhost:4567/data/something will return the result from externalsite.com/something.

Pablo Jomer
  • 9,870
  • 11
  • 54
  • 102
0

Another option would be to setup a CNAME record on your own domain to "Mask" the remote domain hostname.

frogstarr78
  • 860
  • 1
  • 7
  • 11