Now: Tutorial for Web and Software Design > XML > XML Mobile > XML Content
> Fixing AJAX: XMLHttpRequest Considered Harmful [Bookmark it]
Fixing AJAX: XMLHttpRequest Considered Harmful

Fixing AJAX: XMLHttpRequest Considered Harmful
by Jason Levitt |

Apache Proxy

Using an Apache proxy is the easiest and cleanest way of getting around these restrictions. However, it requires that you have access to the main Apache httpd.conf file as well as have Apache's mod_proxy extension loaded. Apache proxy directives are not allowed in local .htaccess files so this method is not a good choice for developers using shared hosting services.

# Pass the call from http://www.yourserver.com/call to http://api.local.yahoo.com
ProxyPass    /call/    http://api.local.yahoo.com/
# Handle any redirects that yahoo might respond with
ProxyPassReverse    /call/    http://api.local.yahoo.com/

Another way to do this is to use Apache's mod_rewrite using the passthru directive:

RewriteEngine on 

RewriteRule ^/call/(.*)$ http://api.local.yahoo.com/$1 [P]

(Note: this rewrite rule may be broken across more than one line in your browser; but mod_rewrite rules won't work that way, so be careful.)

The Apache proxy approach is clean and simple. Consider a call to the Yahoo Geocode REST Web service:

http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&location=78704

With either of the Apache proxy examples functioning, your AJAX application can make a call to:

http://www.yourserver.com/call/MapsService/V1/geocode?appid=YahooDemo&location=78704

and the request will be seamlessly forwarded, and the results returned, to your AJAX application.

Script Tag Hack, or On-Demand JavaScript

The script tag hack is a more complicated approach that involves dynamically generating an HTML script tag and using the src attribute of the tag to make the request. It never makes an XMLHttpRequest, but it's worth looking at because it provides a fairly standard way of making web service requests from within an AJAX application. The code I talk about below is adapted from Darryl Lyons' blog posting on the subject.

The HTML script tag can only return JavaScript. So, to make this approach work, we need to modify the application proxy above to return JavaScript. Two str_replace statements are used to encode any single quotes or newlines that may be in the XML -- they will cause JavaScript scripts to break. Finally, the Content-Type header is changed from text/xml to text/javascript. I'll call the new proxy proxy_script.php (here, the proxy returns XML, but we could also return JSON or other data encodings as well). The XML is placed into a JavaScript global variable -- in this case I've given it the imaginative name xml:

...

...

// Make the call

$xml = curl_exec($session); 



// encode the returned XML as a Javascript variable

$xml =str_replace("'", "'", $xml); 

$xml =str_replace("\n", "", $xml); 

$xml = 'var xml = \''.$xml.'\';';

// The web service returns javascript

header("Content-Type: text/javascript");

echo $xml;

...

...

To make a call, you dynamically create a script tag in your browser's DOM and then point the src attribute at the proxy. To demonstrate this as simply as possible, I created a short web page:

<body>

<a href="javascript:getLocations()">Click this link</a> to dynamically create a script tag and make 

a web service call. A button will appear below after the call has finished</p>

<br/><br/>

<div id="locationData"></div>

</body>

Clicking on the link above will call the function getLocations() which will dynamically create a script tag and retrieve a web service. The function getLocations() calls the function getDataFromServer():

function getLocations() {

     getDataFromServer("ScriptTagID","http://localhost/proxy_script.php");

}</p>

The function getDataFromServer takes as arguments the name of the script tag that will be dynamically generated (and later destroyed) and the prefix of the URL to be fetched. getDataFromServer() does all the real work. The script tag in IE 5 and 6 can fetch data asynchronously using a proprietary mechanism described here. To encapsulate that behavior, I check for the presence of an IE browser using the same definition function as Sarissa, renamed to BROWSER_IS_IE. For all other web browsers, the script tag essentially fetches data synchronously, though it's possible to attach pseudo-asynchronous properties to it (this is left as an excercise for the reader).

After the data is fetched via the newly created script tag, the callback function, putXMLhere(), is called, and the fetched data is available.

<script>

 var callback = "putXMLhere();";

 

function getDataFromServer(id, url) {



    // Fetch the element pointed to by the id. If it exists, we destroy it so we can create a new one.

    oScript = document.getElementById(id);



    // Point at the script tag, if it exists

    var head = document.getElementsByTagName("head").item(0);

     // Destroy the tag, if it exists

    if (oScript) {

    // Destory object

    head.removeChild(oScript);

    }<p>    // Create the new script tag

    oScript = document.createElement("script");



    // Setup the src attribute of the script tag

    sendPath = encodeURIComponent("/MapsService/V1/geocode?appid=YahooDemo&location=78704");

    wholeurl = url + "?path=" + sendPath;

    oScript.setAttribute("src", wholeurl);



    // Set the id attribute of the script tag

    oScript.setAttribute("id",id);



    // Create the new script tag which causes the proxy to be called

    head.appendChild(oScript);

    // Asynchronous script tag properties -- a proprietary IE "feature"

    if (BROWSER_IS_IE) {

        if  (oScript.readyState == "loaded") {

        eval(callback);

        oScript.onreadystatechange = null;

       } else {

        oScript.onreadystatechange = CheckAgain;

       }

    // All other web browsers just do the callback function

    } else {

       eval(callback);

    }

}

 

// Used by IE to handle state changes

function CheckAgain() {

  if (oScript.readyState == "loaded") {

      eval(callback);

      oScript.onreadystatechange = null;

    } 

}</p>// Once the script tag has loaded the data, it's available in the global Javascript variable "xml" which was sent from the proxy.

 function putXMLhere() {

     ohandle = document.getElementById("locationData");

     ohandle.innerHTML = ohandle.innerHTML + "<form><input type='button' value='View XML' onClick='alert(xml); return true;' /></form>";

  }

</script>

Further Plumbing

All of these approaches will provide a seamless, cross-browser AJAX experience for your users while they access third-party web services. But always check with your friendly security administrator before deploying them.

Yes, it's all been done before. These three sites were my major sources of inspiration (or plagiarism): :-)

Darryl Lyons' page on dynamic script tag usage was the inspiration for my investigation.

Premshree Pillai's discussion of the Apache proxy got me looking at that territory.

Think about Cross-Domain Mediation using portlets -- Cross-Domain Mediation is a nice name for "proxy."

Example code for this article: scripthack.zip

Prev  [1] [2] 

[Bookmark][Print] [Close][To Top]
  • Prev Article-XML:

  • Next Article-XML:
  • Related Materias
    Finding the First, Last, B
    Using the W3C XSLT Specifi
    Adventures with OpenOffice
    Axis Powers: Part Two
    Axis Powers: Part One
    Finding Relatives
    XML.com: XML Standards for
    XML.com: Last Word on Last
    The Impact of Site Finder 
    A Preview of WS-I Basic Pr
    Topics
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Graphic Design Tutorial
     

    Coreldraw Tutorial

      Illustrator Tutorial
      3D Graphics Articles
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial&Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial&Articles
     

    XML Style Tutorial

      AJAX Tutorial
      XML Mobile
    Flash Tutorial&Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial&Articles
     

    Linux Tutorial

      Symbian Tutorial
      MacOS Tutorial