Cross Domain Ajax Request with XML response for IE,Firefox,Chrome, Safari – jQuery

on July 14, 2011 by Programming, Technology with 20 comments

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:

  1. Bypassing the cross domain AJAX request restriction
  2. Consuming and parsing the XML response

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++;
            }
        });
    });
}
The following two tabs change content below.

Matthew Mombrea

Matt is a longtime entrepreneur and software engineer. He is a founder of Cypress North, and chief technology officer. Matt also contributes to the community as a columnist on ITworld.com.

20 Comments

  • John
    on July 29, 2011 Reply

    Thank for the info, very good read!

  • KntL
    on October 1, 2011 Reply

    a nice lifesaver article. thank you!

  • JDB
    on February 24, 2012 Reply

    dude, this was a lifesaver. Thanks!

  • Doug
    on April 13, 2012 Reply

    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.’);

    • Matt Mombrea
      on April 13, 2012 Reply

      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.

      • Doug
        on April 13, 2012 Reply

        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

        • Doug
          on April 13, 2012 Reply

          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.

          • Matt Mombrea
            on April 13, 2012

            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) {

  • Doug
    on April 13, 2012 Reply

    sorry, when i said i copied and pasted i did change the site to my site.

  • Rohit
    on April 17, 2012 Reply

    Awesome Stuff Man !! YQL worked for me!

  • Haseeb
    on June 9, 2012 Reply

    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);
    }
    }
    );

    • Matt Mombrea
      on June 9, 2012 Reply

      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/

  • George
    on November 13, 2012 Reply

    Huge help! Thank you very much!

  • Hardik Patel
    on January 26, 2013 Reply

    Thanks a lot.
    Firefox ajax query is working for cross domain

  • venkat
    on July 20, 2013 Reply

    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

  • Brice Durand
    on July 29, 2013 Reply

    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?

  • swa
    on September 5, 2013 Reply

    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)
    }
    });

Leave a Reply