Cross-Origin Resource Sharing
Recently I was implementing jQuery templates as part of our web application, and early on we had determined that we didn't want the development cycle of the templates to be tied to the deployment schedule of the application (which is typically every 3-6 months). We also needed to make sure that we didn't hold up the loading of the application page while the templates were getting fetched and rendered. So we opted to store the templates on a different sub-domain outside of the application and to use AJAX (XMLHttpRequest) to get the template files as needed.
The problem I came across was the same origin policy. Basically, this is a security feature built in to nearly every web browser that states that a document or script can only access properties of another document or script if it resides on the identical origin (domain and sub-domain).
The same origin policy prevents a document or script loaded from one origin from getting or setting properties of a document from another origin. This policy dates all the way back to Netscape Navigator 2.0.
[https://developer.mozilla.org/en/Same_origin_policy_for_JavaScript]
Since I was executing the AJAX call from web.domain.com, files from sub.domain.com were actively being rejected by the browser. In steps CORS...
Cross-Origin Resource Sharing
CORS (Cross-Origin Resource Sharing) is a W3C WG specification for communication between the browser and server. This spec solves the issue of getting a script via XMLHttpRequest that resides on another server. Without CORS, the browsers' same origin policy prevents the browsers from getting and executing a script unless that script is on the exact same origin (same domain and sub-domain).
Even though CORS is part of the HTML5 spec, it has been around for some time and is currently supported in IE8 (via XDomainRequest), FF 3.5, Safari 4, and Chrome (and later versions). The lack of support in IE7 for CORS can be fairly easily worked around via polyfills.
For more information on the CORS spec, see http://www.w3.org/TR/cors/
So what this means is that as long as I have the server at sub.domain.com return the template files with the proper response header, the browser would accept the file and allow my script on web.domain.com to use it.
How CORS Works
CORS tells browsers to send to the server a specific header - Origin - which contains the domain of the page making the request. The server returns a specific header - Access-Control-Allow-Origin - which contains a list of all permitted domains; * can also be returned to allow any. The browser then compares the two values, and if they are in agreement the browser allows the script to execute. If not, the script is disallowed.
How to Use CORS
If you are using a modern browser, your browser us already following the CORS spec. So all you need now is to use a server that returns the expected response header (Access-Control-Allow-Origin). Updating a server to return the proper header is fairly easy if you have access to the server. If you don't have access, it's still possible to follow CORS by using your server-side language to include the response header. See http://enable-cors.org/ for specific details.
Example Using jQuery Templates Served from a Different Source
For our project, we first see if we're on IE7. If so, step out and use the polyfill (https://github.com/toolness/postmessage-proxied-xhr), bypassing the CORS method altogether.
Next, we check to see if we're using a browser that uses XMLHttpRequest ('cause IE8 doesn't) by testing the property "withCredentials". IE8 uses XDomainRequest object, which is very similar and returns that same type of response.
Next we pass that response to our function that applies the template, and bingo bango there's our web content, rendered nicely on the page without slowing up the original page and served from a different server.
The code:
function applyTemplate(theTemplatePath, theElem, theData, theItemType) { function onObjectLoad(thisTemplate) { var aNew = $($.tmpl(thisTemplate, theData)); $(theElem).replaceWith(aNew); } function createCORSRequest(method, url){ var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr){ // check for the 'withCredentials' property to see if browser supports CORS via XHR xhr.open(method, url, true); } else if (typeof XDomainRequest !== "undefined"){ // IE8 uses XDomainRequest xhr = new XDomainRequest(); xhr.open(method, url); } else { xhr = null; } return xhr; } var ieVersion = getIEVersion(); if (ieVersion === 7) { // IE7 - proxy // set up proxy server for getting templates cross domain $.getScript('templates/js/ppx.js', function(){ $.getScript('templates/js/ppx.jquery.js', function(){ // this file sets up the proxy server jQuery.proxyAjaxThroughPostMessage('templates/js/server.html'); try { jQuery.get(theTemplatePath, function(data) { onObjectLoad(data); }); } catch(e) { ep.debug(e); } }); }); } else { // other browsers - CORS var request = createCORSRequest("get", theTemplatePath); if (request){ request.onload = function(){ onObjectLoad(request.responseText); }; request.send(); } } }














