« - »

The WhoAmI Service: Client Side

5 February 2008

Assuming that we have mapped the WhoAmI servlet to the path /whoami within a web application with the context root of /id, we can test the service out in a browser simply by entering http://<your-server-name>/id/whoami. While this is useful in making sure that all of the parts are working as they should, to really get the value out of the service, we need to access the data via AJAX within an HTML page. For example, we can assemble a small sample page containing the phrase “You are signed on as _____.” and add in the code to call the service to obtain the data, after which we can then fill in the blank with a link to the authenticated user’s URI.

The code to do that is relatively simple, but before we go too far down that road, let’s build a little script just for the AJAX call that we can reuse later for other, similar projects. There are a number of different flavors of such scripts floating around the net, but only a few of them have all of the features that I like to see, and even fewer have support for all four of the REST HTTP methods, GET, PUT, POST, and DELETE. First though, lets take a look at a small portion of the script that will build the HTTP Request object itself:

/**
 * This function builds and returns an httpRequest object
 */
function getNewHttpRequestObject(url) {
 var httpRequest = null;

 if (window.XMLHttpRequest) {  httpRequest = new XMLHttpRequest();
  httpRequest.originalURL = url;
 } else if (window.ActiveXObject) {
  var objects = ["MSXML2.XMLHttp.8.0",
    "MSXML2.XMLHttp.7.0",
    "MSXML2.XMLHttp.6.0",
    "MSXML2.XMLHttp.5.0",
    "MSXML2.XMLHttp.4.0",
    "MSXML2.XMLHttp.3.0",
    "MSXML2.XMLHttp",
    "Microsoft.XMLHttp"];
  for (var i=0; i<objects.length; i++) {
   try {
    httpRequest = new ActiveXObject(objects[i]);
    // this one worked ... let's bail!
    i = objects.length;
   } catch (e) {
    // this one failed ... keep trying!
   }
  }
 }

 return httpRequest;
}

Now, other than the fact I went a little overboard on anticipating future versions of the MS flavors of the HTTP Request object, this is pretty standard code that most people have seen just about everywhere. The one minor oddity might be the fact that I added the URL as a property to the non-MS object, but that’s just so that we can reference it later in error messages if necessary. I would have done it to the MS version as well, but it really doesn’t like you doing that and throws up an error.

I pulled this code out of the four methods in the script that make the AJAX calls because it’s virtually the same for all four of them, so it can stand on its own as a littel utility subroutine. Another thing that you may notice with this script is that I call this routine every time I make an AJAX call, which creates a new HTTP Request object every time. While there are some performance improvements in reusing HTTP Request objects, we’re not going to be making that many calls in this particular little example, so you’ll never notice the difference.

To use the WhoAmI service, which only responds positively to a GET request, we will use the ajaxGet() method:

/**
 * This function processes the AJAX URL to GET data
 */
function ajaxGet(url, responseFunction, errorFunction) {
 var httpRequest = getNewHttpRequestObject(url);

 httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4) {
   try {
    if (httpRequest.status == 200) {
     eval(responseFunction + '(httpRequest.responseXML)');
    } else {
     eval(errorFunction + '(httpRequest)');
    }
   } catch (e) {
    if (window.XMLHttpRequest) {
     alert('There has been an error processing this request for URL ' +
         httpRequest.originalURL + ':\n' + e);
    } else {
     alert('There has been an error processing this request:\n' + e);
    }
   }
  }
 }

 //Request the XML document
 httpRequest.open('GET', url, true);
 httpRequest.send('');
}

Again, pretty standard code that we’ve all seen before, but that’s just support for the basic GET. The rest of the methods I’ll toss in here even though they aren’t necessary for this example, since we will be using them later on:

/**
 * This function processes the AJAX URL to PUT data
*/
function ajaxPut(url, data, responseFunction, errorFunction) {
 var httpRequest = getNewHttpRequestObject(url);
 httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4) {
   try {
    if (httpRequest.status == 200) {
     eval(responseFunction + '(httpRequest.responseXML)');
    } else {
     eval(errorFunction + '(httpRequest)');
    }
   } catch (e) {
    if (window.XMLHttpRequest) {
     alert('There has been an error processing this request for URL ' +
          httpRequest.originalURL + ':\n' + e);
    } else {
     alert('There has been an error processing this request:\n' + e);
    }
   }
  }
 }

 httpRequest.open('PUT', url, true);
 httpRequest.setRequestHeader("Content-type", "text/xml");
 httpRequest.setRequestHeader("Content-length", data.length);
 httpRequest.send(data);
}

/**
 * This function processes the AJAX URL to POST data
 */
function ajaxPost(url, parameters, responseFunction, errorFunction) {
 var httpRequest = getNewHttpRequestObject(url);

 httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4) {
   try {
    if (httpRequest.status == 200) {
     eval(responseFunction + '(httpRequest.responseXML)');
    } else {
     eval(errorFunction + '(httpRequest)');
    }
   } catch (e) {
    if (window.XMLHttpRequest) {
     alert('There has been an error processing this request for URL ' +
          httpRequest.originalURL + ':\n' + e);
    } else {
     alert('There has been an error processing this request:\n' + e);
    }
   }
  }
 }

 httpRequest.open('POST', url, true);
 httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
 httpRequest.setRequestHeader("Content-length", parameters.length);
 httpRequest.send(parameters);
}

/**
 * This function processes the AJAX URL to DELETE data
 */
function ajaxDelete(url, responseFunction, errorFunction) {
 var httpRequest = getNewHttpRequestObject(url);

 httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4) {
   try {
    if (httpRequest.status == 200) {
     eval(responseFunction + '(httpRequest.responseXML)');
    } else {
     eval(errorFunction + '(httpRequest)');
    }
   } catch (e) {
    if (window.XMLHttpRequest) {
     alert('There has been an error processing this request for URL ' +
          httpRequest.originalURL + ':\n' + e);
    } else {
     alert('There has been an error processing this request:\n' + e);
    }
   }
  }
 }

 //Request the XML document
 httpRequest.open('DELETE', url, true);
 httpRequest.send('');
}

All of that together I put into a little script called ajaxCall.js, which I use quite frequently for a variety of purposes. Now that we’ve got that out of the way, let’s focus on the code that we will need to actually make the call and do something with the data. Assuming that we have an “onload” attribute in the body tag of our HTML page that points to our initPage() function, we should be able to complete the work with just a couple of methods:

/**
 * Initialize the page
 */
function initPage() {
  ajaxGet('/id/whoami', 'initPageCont','initPageError');
}

/**
 * Initialize the page (continued)
 */
function initPageCont(xmlDoc) {
 var root = xmlDoc.documentElement;
 var userId = root.getElementsByTagName('remoteUser')[0].attributes[0].value;
 var userURI = root.getElementsByTagName('remoteUser')[0].attributes[1].value;
 var userName = root.getElementsByTagName('remoteUser')[0].firstChild.data;
 var userLink = '<a href="' + userURI + '" title="' + userName + '">' +
      userName +  '</a>';
 document.getElementById('whoami').innerHTML = userLink;
}

Well, that’s about for now. Maybe next time, I’ll wrap it all up into a nice little .war file and post it out somewhere where you can just download and try it out on your own application server.

Maybe …


http://blog.restafarian.org/2008/02/the-whoami-service-client-side/

Comments are closed.

Sorry, the comment form is closed at this time.