cross-domain ajax with http post for sending large amount of data

by Martin Monperrus

There are dozens of methods to make cross-domain ajax requests in Javascript (see [[http://engin.bzzzt.biz/2010/03/31/cross-domain-data-push-methods-compared/|this]] and [[http://www.arunranga.com/articles/browser-cross-site.html|this]] for a very good overview). In this document, I focus on those that enable to push large amount of data using HTTP POST xhr and propose a way to unify the most portable techniques using [[http://jquery.com/|jQuery]].

The prerequisite is that the target server supports both [[http://www.ibm.com/developerworks/library/wa-aj-jsonp1/|JSON-P]] and [[http://www.w3.org/TR/cors/|cross-origin resource sharing]] (headers and preflight HTTP OPTIONS).

This technique mixes [[http://www.w3.org/TR/XMLHttpRequest2/|XMLHttpRequest Level 2]], [[http://www.jimbojw.com/wiki/index.php?title=SWFHttpRequest_Flash/Ajax_Utility|SWFHttpRequest]] ([[cross-domain ajax with jQuery and flash-based SWFHttpRequest]]) and [[http://code.google.com/p/swfobject/|swfobject]].

==== Core technique ==== Let’s start now. Here is the commented technique:
/** Can we use XMLHttpRequest for cross-domain HTTP POST? 
 *  @see http://www.w3.org/TR/XMLHttpRequest2/ for the spec
 *  @see http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/ 
 *for the detection
  */
Config.XMLHttpRequestCrossDomainIsSupported 
      = ((new XMLHttpRequest()).withCredentials !== undefined);

// this is the fallback mode: JSONP + script node insertion in the DOM
// cross-browser but limited to sending ~2k data
var ajaxParams = {
  url: 'http://example.com/p/',
  dataType: 'jsonp',
  type: 'get',
  data: mydata,
  success: function(val){
      doSomethingWith(elem);
  }
}

// if we can use XMLHttpRequest, which is standardized and native, we use it
if (Config.XMLHttpRequestCrossDomainIsSupported) {
  ajaxParams.type = 'post'
}
// otherwise if we can use flash as a transport object, we use it
// still better than JSONP + script node insertion 
// because it uses the POST HTTP method (no limitations on the data size)
else if (Config.flashIsSupported) {
  ajaxParams.xhr  = function(){return new SWFHttpRequest();};
  ajaxParams.type = 'post';
}

$.ajax(ajaxParams);

What we can see is that we just tailor tha ajax parameters before actually doing the request.

==== Complete technique ==== The remaining things to do are to load the flash object and set ‘’Config.flashIsSupported’’. It can be done statically with HTML tags (see SWFHttpRequest documentation) or dynamically using swfobject. Here is the code for the latter:

// step 1: setting Config.XMLHttpRequestCrossDomainIsSupported
Config.XMLHttpRequestCrossDomainIsSupported 
    = ((new XMLHttpRequest()).withCredentials !== undefined);

...

// in order to save bandwidth (delivering the SWF object)
// we don't enable flash if XMLHttpRequest Level 2 is available
if (!Config.XMLHttpRequestCrossDomainIsSupported && swfobject) {
  var flash = $(document.createElement("div"));
  flash.attr({
      id:  "flash_container"
    });
  flash.appendTo($('body'));
  // step 2: loading the SWF file
  swfobject.embedSWF(
    'http://example.com/swfhttprequest.swf',
    'flash_container',"0", "0", "9.0.0", false, false, 
    {allowscriptaccess:'always'}, {allowscriptaccess:'always'},
    function(e) {
        if (e.success) { 
          // step 3: setting Config.flashIsSupported
          Config.flashIsSupported = true;
        }
    }
  );
}

...
// step 4: and now comes the request
var ajaxParams = {
  url: 'http://example.com/p/',
  dataType: 'jsonp',
  type: 'get',
  data: mydata,
  success: function(val){
      doSomethingWith(val);
  }
}

if (Config.XMLHttpRequestCrossDomainIsSupported) {
  ajaxParams.type = 'post'
}
else if (Config.flashIsSupported) {
  ajaxParams.xhr  = function(){return new SWFHttpRequest();};
  ajaxParams.type = 'post';
}

$.ajax(ajaxParams);
Tagged as: