Saturday, August 25, 2012

XMLHttpRequest doesn't work on IE, and the fix.

I was working on a website that needed content from another site using AJAX. The site worked fine in chrome, safari, but not IE 9. IE just threw errors without any real information. The issue was a cross-site exception. In this case, the content was JSON information from graph.facebook.com. Even if you have your application inside a facebook iframe, Internet Explorers XMLHttpRequest implementation will not allow calls to graph.facebook.com because it is a cross-site call.

Here's the short list of what you need to keep in mind to get it working:
  • 1. No mixed content
    If your website is https, all calls must be https. Even though it is faster to use http://graph.facebook.com, the fact that facebook enforces https means that you must makes calls to https://graph.facebook.com if you want the information from it. I fixed this by having Javascript check to see if the browser was IE, and then did a passthrough from the hosting server via PHP.
  • 2. No Callback for Null Returns!
    If you do an AJAX callback, the callback will not run if request.responseText is "". So, you cannot use a null response a trigger for a function, even if the status is good.
  • 3. You need to use ActiveX
    You need to check to see if you are using IE, and if you are, you need to explicitly load XMLHttpRequest from ActiveX because the build-in one does not work the same! (on IE 9)
  • 4. IE caches everything
    What I mean by this is that it aggressively caches everything. As an example, if you have a file called test.php?id=10 that returns data that is changing, IE will only get it once unless you explicitly have expiry assignments in the headers.

The following is what worked for Javascript:
function getInternetExplorerVersion()
// Returns the version of Internet Explorer or a -1
// (indicating the use of another browser).
// http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
{
  var rv = -1; // Return value assumes failure.
  if (navigator.appName == 'Microsoft Internet Explorer')
  {
    var ua = navigator.userAgent;
    var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
    if (re.exec(ua) != null)
      rv = parseFloat( RegExp.$1 );
  }
  return rv;
}

function ajax(url, callbackFunction, p_targetid) {
    "use strict";
    var str_err, request, errorcode, isIE, newurl, done, ok, url_alt, a_url, fbid, tmp; 
    request = false;
    if(getInternetExplorerVersion() === -1)
    {
      request=new XMLHttpRequest();
    } else {     
       if(!request)
       {
         try{
         request=new ActiveXObject("Msxml2.XMLHTTP.6.0"); 
        try { 
        request=new ActiveXObject("Msxml2.XMLHTTP.3.0"); 
        } catch(e) { request=false;
        }
         }catch(e) { request=false;
    } 
       //here we need to change our forwarding options to go off site.
    if (url.indexOf("https://graph") == 0) {
         a_url = url.split("/")      
         tmp = a_url.length;
         tmp = tmp - 1;
         fbid = a_url[tmp];
         url_alt = '/forwardrequest.php';
         url_alt = url_alt.concat('?fbid=',fbid);
         url = url_alt;
       }
     } 
     }

  request.open("GET", url, true);
  request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  request.onreadystatechange = function () {
    done = 4;
    ok = 200;
    if (request.readyState === done && request.status === ok) {
      if (request.responseText) { callbackFunction(request.responseText, p_targetid); }
    }
  };
  request.send();
}

Notice where forwardrequest.php is the fix for IE calls offsite, in this case, JSON information. Most of the sites users are non-IE, so the burden is then completely on the browser, but you need to be mindful of hitting forwardrequest.php so much, so implemented a caching scheme for my calls to it in Javascript. That is not included here.

The forwardrequest.php that worked for me:
<?php
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

function getFBPublicInfo($p_userid)
{
  $url  = "http://graph.facebook.com/$p_userid";
  $array_json = json_decode(file_get_contents($url),true); //true is important to make it an array
  return($array_json);
}
if($_GET["fbid"])
{
  $p_userid = $_GET["fbid"];
  $url  = "http://graph.facebook.com/$p_userid";
  $result = file_get_contents($url);
  $result = getFBPublicInfo($p_userid);
    $i = count($result);
    $i = $i -1;
    $j = 0;
    echo "{\n";
    while (list($key, $value) = each($result)) {
        echo "  \"$key\": \"$value\"";
        if($j==$i)
        {
            echo"\n";
        }else
        {
            echo",\n";
        }
        $j++;
    }
echo "}\n";
}
?>
I have to explicitly build a JSON structure because the special characters were being stripped, so I just did a JSON decode and then rebuilt it.
I am just happy to be finished, and this is a reminder why I do not do web programming unless a friend asks. It's only worth it if you aren't billing because these silly things can take forever.

No comments:

Post a Comment