The Future Ajax Wrapper
8 April 2008With IE8’s new XDomainRequest feature, a new API is added for cross-site requests, instead of using the W3C cross-site access proposal. Just for fun, I thought I would provide a little glimpse of what the classic Ajax request wrapper function may look like for the next era of web developers. Just a few simple calls to XMLHttpRequest would be way too easy, so instead we get do this:
function doRequest(method,url,async,onLoad,onProgress) {
var xhr;
if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
// if it is x-domain or streaming/incremental updates are needed we will use IE's XDomainRequest for IE
// streaming/interactive mode is broken in IE's XHR, but for some reason works in XDR (with onprogress), so we will
// need to use XDR if incremental updates are necessary
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334813
if (url.match(/^https:/) && !onProgress) {
// XDR doesn’t work for secure https communication
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333380
loadUsingScriptTag(url); // script tag insertion can be more secure than XDR
// in some situations because it supports https
return;
}
xhr = new XDomainRequest;
// relative paths don’t work in XDomainRequest, see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333275
if (!url.match(/^http:/)) { // test to see if it is an absolute url
url = absoluteUrl(location.href,url); // must have a function to turn it into an absolute url
}
if (!(method == “GET” || method == “POST”)) {
// XDomainRequest does not support methods besides GET and POST
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334809
// We will try to add the method as a parameter and hope the server will understand… good luck :/
url += “&method=” + method;
method = “POST”;
}
function xdrLoad() {
if (xhr.contentType.match(/\/xml/)){
// there is no responseXML in XDomainRequest, so we have to create it manually
var dom = new ActiveXObject(”Microsoft.XMLDOM”);
dom.async = false;
dom.loadXML(xhr.responseText,200);
onLoad(dom);
}
else {
onLoad(xhr.responseText,200); // we will assume that the status code is 200, XDomainRequest rejects all other successful status codes
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334804
}
}
if (async === false) {
// XDomainRequest does not support synchronous requests
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=336031
// so we will try to block execution on our own (which is not really possible in any reasonable manner)
var loaded;
xhr.onload = function() {
loaded = true;
xdrLoad();
}
xhr.open(method,url);
xhr.send(null);
while(!loaded) { // try to block until the response is received
// I am sure the user won’t mind just clicking OK so we can block execution
alert(”Waiting for the response, please click OK because it probably is here now”);
}
return;
}
else { // do an asynchronous request with XDomainRequest
xhr.onload = xdrLoad;
xhr.open(method,url);
xhr.onprogress = onProgress;
}
}
// we will mercifully skip all the branches for ActiveXObject(”Microsoft.XMLHTTP”) to accomodate IE6 and lower
else {
xhr = new XMLHttpRequest; // use the standard XHR for same origin and browsers that implement cross-site
// W3C requests and streaming
xhr.open(method,url,async);
xhr.onreadystatechange = function() {
if (xhr.readyState == 3) // interactive mode
onProgress(xhr.responseText);
if (xhr.readyState == 4) // finished
onLoad(xhr.responseText,xhr.status);
}
}
xhr.send(null); // finally send the request whether it be XDR or XHR
// and supporting functions
function absoluteUrl : function(baseUrl, relativeUrl) {
// This takes a base url and a relative url and resolves the target url.
// For example:
// resolveUrl(”http://www.domain.com/path1/path2″,”../path3″) ->”http://www.domain.com/path1/path3″
//
if (relativeUrl.match(/\w+:\/\//))
return relativeUrl;
if (relativeUrl.charAt(0)==’/') {
baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
}
//TODO: handle protocol relative urls: ://www.domain.com
baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);// clean off the trailing path
if (relativeUrl == ‘.’)
return baseUrl;
while (relativeUrl.substring(0,3) == ‘../’) {
baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
relativeUrl = relativeUrl.substring(3);
}
return baseUrl + relativeUrl;
}
function loadUsingScriptTag(url) {
… do JSONP here if we want
}
}
Disclaimer: This is completely untested code, just a glimpse of the issues and divergences I am already aware of. I am sure the working code with be even more complicated. I didn’t get into the code branches for errors (XDomainRequest uses a new “onerror” handler), and or discuss the server side branching code for two different sets of headers, or the lack of support for basic HTTP headers.
I do appreciate IE’s effort to provide some mechanism for cross-site requests. But, do we really need a new API? Also, don’t get me wrong, I think the W3C cross-site XHR has it’s warts as well, for example I think XML processing instructions do not belong in an XHR specification.
6 Responses to “The Future Ajax Wrapper”
April 8th, 2008 at 9:33 pm
OMG!
I am a MS.NET developer, and I know that they do not want to be constrained by the W3’s ridiculous meeting politics and time lines, but does that mean they have to re-invent EVERYTHING?
April 18th, 2008 at 8:20 am
Here is another possible implementation of absoluteUrl():
var tempDiv = document.createElement('div');
function absoluteURL(url) {
tempDiv.innerHTML = '‘;
return tempDiv.firstChild.href;
};
A little cheeky, I know. But very short!
Also, there is a problem with your comment form: it won’t accept email addresses containing a plus sign ( ). They’re in the spec!
April 18th, 2008 at 8:20 am
Thank you M$… Soon we’ll have the IE6/7/8 Nightmare! Is 6 and 7 not enough?
April 18th, 2008 at 8:23 am
Wow, my code block got mangled beyond recognition.
Here is my alternate implementation of absoluteUrl(), exploiting the html parser: http://dpaste.com/hold/45665/
April 18th, 2008 at 9:09 am
henrah, that is very cool. Is that x-browser, or just for IE? It is only needed for IE in this code, but it would be cool to use that elsewhere.
April 19th, 2008 at 8:20 am
Kris, Access Control can also be used for XSLT, XBL, et cetera. There a processing instruction makes a lot of sense for ease of authoring. I agree that for typical XMLHttpRequest scenarios you probably do not need it, but it is not just for XMLHttpRequest.