Cross Domain Ajax Request with XML response for IE,Firefox,Chrome, Safari – jQuery
In a previous post I discussed how to accomplish cross domain JSON requests and some caveats to be aware of. That example involved a scenario in which you had control of the web service environment and were able to allow the cross domain requests on the server side. But what do you do when this isn't an option, and furthermore, the response is not JSON but XML?
There are two challenges here:
Making a cross domain request - the hard way
I say the hard way, but if you have no way of modifying the source server it may be your only option. The trick here is to use the Yahoo API library (Specifically YQL) to act as a proxy between your AJAX call and the server. This is a free, publicly available service that accepts an SQL style query for scraping web responses. If you're struggling with this cross domain issue, you've no doubt tried making a request to the server you're trying to reach via your web browser directly instead of through an AJAX request and found that it works. It's frustrating that you can see your data in this way but not through the request that you need.
The YQL service will basically perform the same type of request that you would make directly through your web browser, then parse the results into an object and return the result to you regardless of the origin domain. This adds considerable latency due the fact that you're now making several more web requests than you'd like and also doing some processing work, but it is what it is and can be a life saver.
Here is the function to make this indirect cross domain request via YQL:
// Accepts a url and a callback function to run. function requestCrossDomain(site, callback) { // If no url was passed, exit. if (!site) { alert('No site was passed.'); return false; } // Take the provided url, and add it to a YQL query. Make sure you encode it! var yql = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="' + site + '"') + '&format=xml&callback=?'; // Request that YSQL string, and run a callback function. // Pass a defined function to prevent cache-busting. $.getJSON(yql, cbFunc); function cbFunc(data) { // If we have something to work with... if (data.results[0]) { if (typeof callback === 'function') { callback(data); } } // Else, Maybe we requested a site that doesn't exist, and nothing returned. else throw new Error('Nothing returned from getJSON.'); } }
Note that there are some parameters you can set in the query depending on the type of data you are working with. You can read up on YQL here.
Making a cross domain request - the easy way
If you are able to modify the web service you're working with, you can avoid the YQL work around by adding a response header to the web service response indicating that cross domain requests are OK. The header you want to add to the response is:
Access-Control-Allow-Origin: *
This will allow any website to perform AJAX requests on this service. You can restrict this to specific domains by replacing the * with the URL you want to allow.
In a .NET WCF service, it's easiest to add this to the global.asax of your service like so:
protected void Application_BeginRequest(object sender, EventArgs e) { HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin","*"); }
Consuming and Parsing the XML Response
USING YQL
Your data is going to come back as a string and you're going to want to get that string into an XML Document so that you can parse it easily using the DOM parser. Similar to the previous article, you have to perform the task differently for IE than you do for other browsers.
function readFeed(limit) { requestCrossDomain('http://www.yourservice.com/xml', function (result) { var num = 1; var browserName = navigator.appName; var doc; if (browserName == 'Microsoft Internet Explorer') { doc = new ActiveXObject('Microsoft.XMLDOM'); doc.async = 'false' doc.loadXML(result.results); } else { doc = (new DOMParser()).parseFromString(result.results, 'text/xml'); } var xml = doc; $(xml).find('news').each(function () { if (num <= limit) { var date = $(this).attr('date'); var headlines = $(this).find('headlines').text(); var link = $(this).find('link').text(); var content = "<p>" + headlines + "</p><a class='more' href='" + link + "'>continue reading</a>"; $("#news" + num).html(content); num++; } }); }); }
This code will call our requestCrossDomain() function and create the callback function to process the response. In this example, we are accepting a limit variable for processing news articles. As you can see, we start out by building an XML doc object in one of two ways depending on the browser. Then with this line:
$(xml).find('news').each(function () {
We search through the XML document using jQuery to find each root element that we're interested in, in this case, the <news> element. For <news> element we find, store it's children elements <date>, <headlines>, and <link> into variables for use in our HTML content. What you do here will depend on your data and goals but the same principals apply.
NOT USING YQL
If you do not need to use YQL because you have added the header
Access-Control-Allow-Origin: *
Then you can make a standard AJAX call like so:
function readFeed(limit) { $.get('http://www.yourservice.com/xml', function (result) { var num = 1; var browserName = navigator.appName; var doc; if (browserName == 'Microsoft Internet Explorer') { doc = new ActiveXObject('Microsoft.XMLDOM'); doc.async = 'false' doc.loadXML(result.results); } else { doc = (new DOMParser()).parseFromString(result.results, 'text/xml'); } var xml = doc; $(xml).find('news').each(function () { if (num <= limit) { var date = $(this).attr('date'); var headlines = $(this).find('headlines').text(); var link = $(this).find('link').text(); var content = "<p>" + headlines + "</p><a class='more' href='" + link + "'>continue reading</a>"; $("#news" + num).html(content); num++; } }); }); }
21 Comments
Leave a Reply
Meet the Author
Matthew Mombrea
Matt is our Chief Technology Officer and one of the founders of our agency. He started Cypress North in 2010 with Greg Finn, and now leads our Buffalo office. As the head of our development team, Matt oversees all of our technical strategy and software and systems design efforts.
With more than 19 years of software engineering experience, Matt has the knowledge and expertise to help our clients find solutions that will solve their problems and help them reach their goals. He is dedicated to doing things the right way and finding the right custom solution for each client, all while accounting for long-term maintainability and technical debt.
Matt is a Buffalo native and graduated from St. Bonaventure University, where he studied computer science.
When he’s not at work, Matt enjoys spending time with his kids and his dog. He also likes to golf, snowboard, and roast coffee.
Thank for the info, very good read!
a nice lifesaver article. thank you!
dude, this was a lifesaver. Thanks!
I copied and pasted your code, and added the header to the response, and it doesn't work. I get a XML parse error. I verified that the xml is valid (i can hit the url directly in browser and it works and is valid xml) If i step through it with firebug, i get to the method function cbFunc(data) and it goes to that else else throw new Error('Nothing returned from getJSON.');
If you get an XML Parse error there is probably something being returned in the XML that the Parser doesn't like. Take a look at cleaning up any invalid characters etc. in the XML response.
I did look at the xml and it has no problems. I even took the xml and pasted it in a xml file locally in my project and hit that instead of the url and it works
Sorry, i misspoke on my first comment, the parse xml error was before i implemented this sites code. With the code from this site, no error that i can see, but the cbFunc method has 0 results. Which according to the site means the site doesn't exist, but it does and i can hit it and get back xml in browser.
Doug, I think I see the issue. It looks like in my example for the section "The Easy Way" I'm still calling the function that executed the YQL call. That is a mistake. The Easy Way should not use YQL, just a standard AJAX request. Try changing the first line in the readFeed function from:
requestCrossDomain('http://www.yourservice.com/xml', function (result) {
to
$.get('http://www.yourservice.com/xml', function (result) {
sorry, when i said i copied and pasted i did change the site to my site.
Awesome Stuff Man !! YQL worked for me!
Error: could not load the page.
Here is my code:
//feel free to add querystring vars to this
var site = "http://www.edreams.es/edreams/espanol/hotels/bookingEngine/partners/HotelSearch.jhtml?mktportal=tradedoubler&city=Madrid&checkInDate=10062012&checkOutDate=12062012&rooms=1&adults=2&childs=1&utm_campaign=tradedoubler-15407";
var yql = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="' + site + '"') + '&format=xml&callback=?';
//make the call to YQL
$.getJSON(yql,
function (data) {
if (data.results[0]) {
//this data.results[0] is the return object you work with,
//if you actually want to do something with the returned json
alert(data.results[0]);
} else {
var errormsg = 'Error: could not load the page.';
//output to firebug's console
//use alert() for other browsers/setups
console.log(errormsg);
}
}
);
Haseeb,
It doesn't look like your site url is returning anything, just an error message.
Regardless, I was able to get a response by using the code above. Here is the JS Fiddle of the script:
http://jsfiddle.net/NdkPU/
[...] Here is a good link about Cross Site Domain request via Ajax. Share this:ShareTwitterLinkedInLike this:LikeBe the first to like this. [...]
Huge help! Thank you very much!
[...] http://www.cypressnorth.com/blog/programming/cross-domain-ajax-request-with-xml-response-for-iefiref... [...]
Thanks a lot.
Firefox ajax query is working for cross domain
Hi,
Thanks for you Code.
My Code:
// Accepts a url and a callback function to run.
function requestCrossDomain(site, callback) {
// If no url was passed, exit.
alert(site);
if (!site) {
alert('No site was passed.');
return false;
}
// Take the provided url, and add it to a YQL query. Make sure you encode it!
var yql = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="' + site + '"') + '&format=xml&callback=?';
// Request that YSQL string, and run a callback function.
// Pass a defined function to prevent cache-busting.
$.getJSON(yql, cbFunc);
function cbFunc(data) {
// If we have something to work with...
if (data.results[0]) {
if (typeof callback === 'function') {
callback(data);
}
}
// Else, Maybe we requested a site that doesn't exist, and nothing returned.
else {//throw new Error('Nothing returned from getJSON.');
alert('Failure');
}
}
}
It is not work for me .
Where i am wrong this code.
Thanks,
Venkat
Thanks Matt!
I've got another issue, my request is not on port 80 and I get an error from Yahoo :
Invalid URL http://my-url:8086/bla : Invalid HTTP destination port
(I replaced the URL by a fake one obviously, but the real url returns a valid XML)
Did they block other ports? Any way to get around this?
Brice,
It looks like a recent yahoo update is causing the issue, I'm not sure if they are going to correct or if it's intended. http://stackoverflow.com/questions/16599722/cant-request-xml-from-url-invalid-http-destination-port
Please look at this code
$.ajax({
url:"http://polymerupdate.com/web/news-desk.asmx/PCWReports",
dataType: 'jsonp', // Notice! JSONP <-- P (lowercase)
success:function(responce){
// do stuff with json (in this case an array)
alert(XML);
},
error:function(jqXHR, textStatus, errorThrown,responce){
alert(textStatus)
}
});
Hi, i try easy way without yql but then i dont get any result. Where must i put Access-Control-Allow-Origin: * ? Im using php