N342 Error Handling

Modified:

Overview

Designers of Web-based systems practice in a dangerous environment where their systems are routinely exposed to naive users and professional attackers.

Errors committed by users, such as not completing a form, should be handled by the user interface on the client-side.

Syntax errors are violations of language rules, simple to discover and correct before the program will execute.

Runtime or execution time errors can occur for unpredictable reasons and times: the database server lost power, an SQL statement was incorrect, etc. Systems cannot be fully tested so that runtime failures are always possible.

Studies analyzing the cause of downtime to online systems found that over 50% of the episodes were due to human operators, not programming errors or user errors. Even well designed and tested systems will fail, as software designers and implementers we should handle failures as gracefully as possible, minimizing the consequences, and providing the user with information that a failure occurred and their options.

There are several points to consider about system faults:

  1. Failures offer an opportunity to purposely or accidentally damage your system.
     
  2. Systems should not expose users to raw error messages from the operating system, programming language, etc. Who would trust their money to a system that fails completely?
     
  3. Raw error messages are useful during system development, providing details of syntax errors, etc.
     
  4. Fully testing any system for all possible combinations of possible conditions, particularly those originating from user input, is generally not feasible (e.g. what happens if a stock trader buys a negative number of stocks?). Still, we should test boundary conditions at a minimum.
     
  5. Scripts may embed code (e.g. SQL) that is only executed under certain conditions (e.g. an empty string cannot be inserted by SQL), for which some conditions may cause failure.
     
  6. Reported errors can be caught and handled at the implementers discretion. Errors should be reported for correction as a means of system improvement.

Most modern languages handle detected failures generally as exceptions where an exception is some out of the ordinary condition that results in the execution of code reserved for dealing with the exceptional condition. Many languages implement exception handling with the try-catch-finally statement discussed below.

 

try...catch…finally Statement

Portions of the following description was extracted from the Microsoft Visual Studio Help

Arguments

try Statements
Required. Statements where an error can occur.
 
exception
Required. Any variable name. The initial value of exception is the value of the thrown error.
 
catch Statements
Optional. Statements to handle errors occurring in the associated tryStatements.
 
finally Statements
Optional. Statements that are unconditionally executed after all other error processing has occurred.

Remarks

The try...catch…finally statement provides a way to handle some or all of the possible errors that may occur in a given block of code, while still running code. If errors occur that the programmer has not handled, JScript simply provides its normal error message to a user, as if there was no error handling.

If the error cannot be handled in the catch associated with the try where the error occurred, use the throw statement to propagate, or rethrow, the error to a higher-level error handler.

After all statements in try have been executed and any error handling has occurred in catch, the statements in finally are unconditionally executed.

Notice that the code inside finally is executed even if a return statement occurs inside the try or catch blocks, or if the catch block re-throws the error. finally is guaranteed to always run, unless an unhandled error occurs (for example, causing a run-time error inside the catch block).

 

Example 0

print("1");

try {
        print("2");
        print("3");
}
catch(e) {
        print("4 " + e);
}
finally {
        print("5"); 
}

function print( s ){
   document.write( s );
}
Produces the following output:
1
2
3
5

 


Example 1

print("1");

try {
        print("2");
        throw "an exception";
        print("3");
}
catch(e) {
        print("4 " + e);
}
finally {
        print("5"); 
}

function print( s ){
   document.write( s );
}
Produces the following output:
1
2
4 an exception
5

 

 

Example 2

The following example illustrates JavaScript exception handling in a nested hierarchy. The general rules are:

  1. A throw inside a try will execute the companion catch.
     
  2. A throw inside a catch or finally will execute an enclosing catch.
try {
      // throw "first exception";
      print("1");

      try {
        print("2");
        throw "an exception 2";
        print("3");
      }
      catch(e) {
        print("4" + e);
        throw e + " re-thrown";
        print("5");
      }
      finally {
        print("6");
      }  
}
catch(e) {
    print("7 " + e);
}
finally {
  print("8");
}

function print( s ) {
   document.write( s );
}

Produces the following output:
1
2
4 an exception 2
6
7 an exception 2 re-thrown
8

 

Exercise 1

What is the output after un-commenting the line in Example 2?

  • throw "first exception";
 

 

ASP Example using try/catch

The following example is from the project, Portfolio.asp. The essential approach is that any reported exception will be caught and somewhat gracefully reported to the user.

By wrapping nearly the complete, normal execution script in a try block, all exceptions will be treated identically.

Portfolio.asp

<%@ Language=JScript%>
<%
   if(Session("trader") == undefined)
      Response.Redirect("Login.xml");

   try {
      conn = Server.CreateObject("ADODB.Connection");
      conn.Mode = 3;
      conn.Open ("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" +
      Server.MapPath("Project.mdb"));

      trader=Session("trader");
      rs = conn.Execute(
         "SELECT PORTFOLIO.SYMBOL, NAME, SHARES, PRICE, " +
         "SHARES*PRICE as STOCKVALUE " +
         "FROM STOCK INNER JOIN PORTFOLIO ON " +
         "STOCK.SYMBOL = PORTFOLIO.SYMBOL WHERE ID='"+trader+"';");

      var xmlDoc = Server.CreateObject("Msxml2.DOMDocument");
      xmlDoc.async = false;
      rs.Save (xmlDoc, 1) ;                 // Convert recordset to XML

      var xsl = Server.CreateObject("Msxml2.DOMDocument");
      xsl.async = false;                     // Convert XML tree using XSL
      xsl.load(Server.MapPath("PortfolioContent.xsl"))
                                                    // Write tree as HTML
      Response.Write(xmlDoc.transformNode(xsl));

      conn.Close();
   }

   catch(e) {
%>

<html>
   <head>
      <title>Stock Trader - Technical Error</title>
   </head>
   <body>
      <h2>Stock Trader - Technical Error</h2>
      <p>Trading cannot be continued at this time. </p>
      <p>Please try again later.</p>
      <p>Contact 1-800-123-4567 to report this problem or send email to:
      <a href="mailto:">Online@StockTrader.com</a></p>
   </body>
</html>

<% } %>

Stock Trader - Technical Error

Trading cannot be continued at this time.

Please try again later.

Contact 1-800-123-4567 to report this problem
or send email to: Online@StockTrader.com

 

 

Exercise 2

  1. Copy, paste and save onto a virtual drive as Exercise2.asp
  2. Login to initialize the stock trader.
  3. Load Exercise2.asp into browser to verify operation.
  4. Change:

      xsl.load(Server.MapPath("PortfolioContent.xsl"))

     to a non-existent file.
     

    1. Reload Exercise2.asp
    2. An error message should be displayed.
       
  5. Add a second try/catch with a try only on the statements:

      conn = Server.CreateObject("ADODB.Connection");
      conn.Mode = 3;
      conn.Open ("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" + Server.MapPath("Project.mdb"));

    and some simple HTML that states that a database error occurred.
     

  6. Load into browser to verify operation. The same error message should be displayed.
  7. Change Project.mdb to another name to induce the database error.
  8. Two error messages may have been displayed. What would be a reasonable measure to display only one error message?

 

Logging Errors

Rather than simply displaying an error message and depending upon the user to report the error, more useful for the developer is to try individual elements that throw exceptions and write details to a system log for later analysis.

The following example has separate try/catch statements for each source of an exception (i.e. the operations that throw exceptions to the caller).

By handling each exception, specific error information can be logged to a server file for later analysis. Here, we present the user with the same error message in any failure case.

Portfolio.asp

<%@ Language=JScript%>
<%
    if(Session("trader") == undefined)
        Response.Redirect("Login.xml");

    var errorstr =" [Portfolio.asp] ";
    var error = false;

    conn = Server.CreateObject("ADODB.Connection");
    conn.Mode = 3;
    conn.Open ("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" +
    Server.MapPath("Project.mdb"));

    if( !error ) try {
        trader=Session("trader");
        rs = conn.Execute(
            "SELECT PORTFOLIO.SYMBOL, NAME, SHARES, PRICE, " +
            "SHARES*PRICE as STOCKVALUE " +
            "FROM STOCK INNER JOIN PORTFOLIO ON " +
            "STOCK.SYMBOL = PORTFOLIO.SYMBOL WHERE ID='"+trader+"';");
    } catch(e) {
        error = true;
        errorstr = errorstr + " SELECT " + e.message;
    }

    if( !error ) try {
        var xmlDoc = Server.CreateObject("Msxml2.DOMDocument");
        xmlDoc.async = false;
        rs.Save (xmlDoc, 1) ;                     // Convert recordset to XML
    } catch(e) {
        error = true;
        errorstr = errorstr + " XML " + e.message;
    }

    if( !error ) try {
        var xsl = Server.CreateObject("Msxml2.DOMDocument");
        xsl.async = false;                          // Convert XML tree using XSL
        xsl.load(Server.MapPath("PortfolioContent.xsl"))
    } catch(e) {
        error = true ;
        errorstr = errorstr + " XSL LOAD " + e.message;
    }

    if( !error ) try {
        Response.Write( xmlDoc.transformNode( xsl ) );
        conn.Close();
    }
    catch(e) {
        error = true ;
          errorstr = errorstr + " XSL TRANSFORM " + e.message;
    }

    if( error ) {
    
  try {

var d = new Date();

var ForAppending = 8;
fs = new ActiveXObject("Scripting.FileSystemObject");

a = fs.OpenTextFile(Server.MapPath("errorlog.txt"), ForAppending, true);

a.Write("\r\n" + d + errorstr );

a.Close();

    } catch(e) { }

  %>

<html>
  <head>
     <title>Stock Trader - Technical Error</title>
  </head>
  <body>
           <h2>Stock Trader - Technical Error</h2>
           <p>Trading cannot be continued at this time. </p>
           <p>Please try again later.</p>
           <p>Contact 1-800-123-4567 to speak to technical
                 support or send email to:
           <a href="mailto:">Online@StockTrader.com</a></p>
  </body>
</html>

<% } %>

Stock Trader - Technical Error

Trading cannot be continued at this time.

Please try again later.

Contact 1-800-123-4567 to speak to
technical support or send email to:
Online@StockTrader.com


Error in XSL of PortfolioContent.xsl
transformation from line:

Response.Write(xmlDoc.transformNode(xsl));

 

Produces contents in errorlog.txt of:

Thu Jun 18 05:32:50 EDT 2012
[Portfolio.asp] XSL TRANSFORM
The stylesheet does not contain a document element.
The stylesheet may be empty, or it may not be a
well-formed XML document.

 

Note

The script also handles exceptions when attempting to write the error log by executing within a try/catch statement.

A more complete solution:

  1. log the error,
  2. empty the trader's cart,
  3. logout the trader,
  4. abandon the session,
  5. create a separate, error notification file and redirect to that file.

 

IUS Network file logging

Error logs (and other files) can be written to locations other than the server executing the program.

The following opens and defines a networked file on the IUS W: drive.

a = fs.OpenTextFile("\\\\se-cser-nas1\\homepages\\ads\\username\\N342\\Project\\errorlog.txt",  ForAppending, true);

If using a locally administered IIS, to write to some other location than that of the program, change to:

a = fs.OpenTextFile("C:\\N342\\Project\\errorlog.txt", ForAppending, true);

 

Exercise 3

  1. Make a minor error in the PortfolioContent.xsl file.
  2. Copy, paste and save above as Exercise3.asp
  3. Modify the HTML to write the Session("trader") variable to the error log file.
  4. Login to initialize the stock trader.
  5. Load Exercise3.asp into browser to verify operation. An error message should be displayed.
  6. Open W:\N342\Project\errorlog.txt.

 

  1. Change Project.mdb to another name to induce the database error and reload Exercise3.asp to observe results.
  2. Add error handling to the following consistent with Exercise3.asp

      conn = Server.CreateObject("ADODB.Connection");
      conn.Mode = 3;
      conn.Open ("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" +
                         Server.MapPath("Project.mdb"));
     

  3. Reload Exercise3.asp to verify operation.
  4. Reopen W:\N342\Project\errorlog.txt