Hack 8. Handle Request Object Errors
Design your Ajax application to detect any server errors and provide a friendly user message. Much of the oomph behind Ajax technology is that it allows JavaScript to connect with a server program without the user intervening. However, JavaScript developers often have no control over the server component itself (which could be a web service or other software designed outside their organizations). Even if your application involves your organization's server component, you cannot always be sure that the server is behaving normally or even that your users are online at the moment they trigger your request object. You have to make sure that your application recovers in the event that the backend program is unavailable. This hack traps errors and displays a meaningful error message, in the event that the Ajax application loses server contact. Problems, Problems...This hack addresses the following exceptional events, and recommends ways for the application to recover from them:
You can use this hack's exception-handling code in any application. This hack uses the stock calculation code from "Receive Data as a Number" [Hack #6]. We'll take a look at the code that initializes the request object and the exception-handling mechanism in a moment, but first, here's the HTML file that imports the JavaScript code from hack6.js: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"> <html> <head> <script type="text/javascript" src="js/hack6.js"></script> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>Tally your stocks</title> </head> <body> <h3>Your total Stock Holdings</h3> <form action="javascript:void%200" onsubmit= "getStockPrice(this.stSymbol.value,this.numShares.value);return false"> <p>Enter stock symbol: <input type="text" name="stSymbol" size="4"> <span id="stPrice"></span></p> <p>Enter share amount: <input type="text" name="numShares" size="10"></p> <p><button type="submit">Get Total Value</button></p> <div id="msgDisplay"></div> </form> </body> </html> When users load this file into their browsers, they see the screen shown in Figure 1-11. Figure 1-11. Request a stock's price![]() The code we are interested in can trap exceptions involving unavailable applications, backend servers that are down, backend server bugs, and erroneous URLs. The handleResponse( ) function is the event handler for managing the server response, as in request.onreadystatechange=handleResponse. The following code uses a nested try/catch/finally statement to deal with invalid numbers handled by the application, as discussed in "Receive Data as a Number" [Hack #6]. function handleResponse( ){ var statusMsg=""; try{ if(request.readyState == 4){ if(request.status == 200){ /* Check if the return value is actually a number. If so, multiple by the number of shares and display the result */ var stockPrice = request.responseText; try{ if(isNaN(stockPrice)) { throw new Error( "The returned price is an invalid number.");} if(isNaN(numberOfShares)) { throw new Error( "The share amount is an invalid number.");} var info = "Total stock value: $"+ calcTotal(stockPrice); displayMsg(document. getElementById("msgDisplay"),info,"black"); document.getElementById("stPrice").style.fontSize="0. 9em"; document.getElementById("stPrice").innerHTML ="price: "+ stockPrice; } catch (err) { displayMsg(document.getElementById("msgDisplay"), "An error occurred: "+ err.message,"red"); } } else { //request.status is 503 if the application isn't available; //500 if the application has a bug alert( "A problem occurred with communicating between the " "XMLHttpRequest object and the server program. "+ "Please try again very soon"); } }//end outer if } catch (err) { alert("It does not appear that the server "+ "is available for this application. Please "+ "try again very soon. \\nError: "+err.message); } } Now, let's take a look at how this code handles the different types of exceptions previously enumerated. Floored ServerA try block traps any exceptions thrown within its curly braces ( {}). If the code throws an exception, this mechanism executes the code within the associated catch block. The inner try block, which is designed to manage exceptions thrown in the event of invalid numeric values, is explained in "Receive Data as a Number" [Hack #6]. So, what happens if the server host is completely down, even though the URL your application uses is otherwise correct? In this case, the code's attempt to access the request.status property throws an exception because the request object never receives the expected response header from the server and the status property is not associated with any data. As a result, the code displays the alert window defined in the outer catch block. Figure 1-12 depicts what the alert window looks like after this type of error. Figure 1-12. Uh-oh, server down![]() The code displays a user message, as well as the more techie error message associated with the exception. You can leave out that part of the message if you desire; it is mainly useful for debugging purposes.
If you do not include this TRy/catch/finally mechanism, the user sees just an alert window containing the indecipherable error message generated by JavaScript. After dismissing this window (or leaving the computer in frustration), the user has no way of knowing what state the application is in. Backend Application Out to LunchSometimes the application server or host is running okay, but the server component you want to connect with is out of service. In this case, the value of the request.status property is 503 ("Service Unavailable"). Because the status property holds a value other than 200, this hack's code executes the expression contained within the else statement block: } else { //request.status is 503 if the application isn't available; // 500 if the application has a bug alert( "A problem occurred with communicating between the " "XMLHttpRequest object and the server program. "+ "Please try again very soon"); } In other words, the user sees an alert window explaining the application's status. This alert also appears if the server component has a bug and crashes. This event typically (such as with the Tomcat servlet container) results in a 500 response status code ("Internal Server Error"), so response.status evaluates to 500 instead of 200 ("Okay"). In addition, any 404 response codes involving a static or dynamic component that the server cannot find at the URL you provided are captured with this TRy statement.
Whoops, Wrong URLWhat if the URL that your Ajax application uses in the request.open( ) method is wrong or has changed? In this case, the request.open( ) call throws the exception, so this is where you have to position your try/catch/finally statement. The code at the top of the next example constructs a request object [Hack #1]. The following function definition, initReq( ), catches the exception just described: function httpRequest(reqType,url,asynch){ //Mozilla-based browsers if(window.XMLHttpRequest){ request = new XMLHttpRequest( ); } else if (window.ActiveXObject){ request=new ActiveXObject("Msxml2.XMLHTTP"); if (! request){ request=new ActiveXObject("Microsoft.XMLHTTP"); } } //the request could still be null if neither ActiveXObject //initialization succeeded if(request){ initReq(reqType,url,asynch); } else { alert("Your browser does not permit the use of all "+ "of this application's features!"); } } /* Initialize a request object that is already constructed */ function initReq(reqType,url,bool){ try{ /* Specify the function that will handle the HTTP response */ request.onreadystatechange=handleResponse; request.open(reqType,url,bool); request.send(null); } catch (err) { alert( "The application cannot contact the server at the moment."+ " Please try again in a few seconds."); } } Another variation of this error is when the URL you use with the request.open( ) method includes a different host than the host from which the user downloaded the web page. For example, say the user downloads the web page from http://www.myorg.com/app, but the URL you use for open( ) is http://www.yourorg.com. This type of error is also caught by the code's try/catch/finally statement.
![]() |