Adobe ColdFusion 8

Handling errors in UDFs

This section discusses the following topics:

  • Displaying error messages directly in the function
  • Returning function status information to the calling page
  • Using try/catch or cftry/cfcatch blocks and the cfthrow and cfrethrow tags to handle and generate exceptions

The technique you use depends on the circumstances of your function and application and on your preferred programming style. However, most functions should use the second or third technique, or a combination of the two. The following sections discuss the uses, advantages, and disadvantages of each technique, and provide examples of their use.

Displaying error messages

Your function can test for errors and use the WriteOutput function to display an error message directly to the user. This method is particularly useful for providing immediate feedback to users for simple input errors. You can use it independently or in conjunction with either of the other two error-handling methods.

For example, the following variation on a "Hello world" function displays an error message if you do not enter a name in the form:

<cfform method="POST" action="#CGI.script_name#">
    <p>Enter your Name:&nbsp;
    <input name="name" type="text" hspace="30" maxlength="30">
    <input type="Submit" name="submit" value="OK">
</cfform>
<cfscript>
    function HelloFriend(Name) {
        if (Name is "") WriteOutput("You forgot your name!");
        else WriteOutput("Hello " & name &"!");
        return "";
    }
    if (IsDefined("Form.submit")) HelloFriend(Form.name);
</cfscript>

Reviewing the code

The following table describes the code:

Code

Description

<cfform method="POST" action="#CGI.script_name#"> <p>Enter your Name:&nbsp; <input name="name" type="text" hspace="30" maxlength="30"> <input type="Submit" name="submit" value="OK"> </cfform>

Creates a simple form requesting you to enter your name.

Uses the script_name CGI variable to post to this page without specifying a URL.

If you do not enter a name, the form posts an empty string as the name field.

<cfscript> function HelloFriend(Name) { if (Name is "") WriteOutput("You forgot your name!"); else WriteOutput("Hello " & name &"!"); return ""; } if (IsDefined("Form.submit")) HelloFriend(Form.name); </cfscript>

Defines a function to display "Hello name!" First, checks whether the argument is an empty string. If so, displays an error message.

Otherwise displays the hello message.

Returns the empty string. (The caller does not use the return value). It is not necessary to use curly braces around the if or else statement bodies because they are single statements.

If this page has been called by submitting the form, calls the HelloFriend function. Otherwise, the page just displays the form.

Providing status information

In some cases, such as those where the function cannot provide a corrective action, the function cannot, or should not, handle the error directly. In these cases, your function can return information to the calling page. The calling page must handle the error information and act appropriately.

Consider the following mechanisms for providing status information:

  • Use the return value to indicate the function status only. The return value can be a Boolean success/failure indicator. The return value can also be a status code, for example where 1 indicates success, and various failure types are assigned known numbers. With this method, the function must set a variable in the caller to the value of a successful result.
  • Set a status variable that is available to the caller (not the return variable) to indicate success or failure and any information about the failure. With this method, the function can return the result directly to the caller. In this method, the function should use only the return value and structure arguments to pass the status back to the caller.

Each of these methods can have variants, and each has advantages and disadvantages. The technique that you use should depend on the type of function, the application in which you use it, and your coding style.

The following example, which modifies the function used in A user-defined function example, uses one version of the status variable method. It provides two forms of error information:

  • It returns -1, instead of an interest value, if it encounters an error. This value can serve as an error indicator because you never pay negative interest on a loan.
  • It also writes an error message to a structure that contains an error description variable. Because the message is in a structure, it is available to both the calling page and the function.

The TotalInterest function

After changes to handle errors, the TotalInterest function looks like the following. Code that is changed from the example in A user-defined function example is in bold.

<cfscript>
function TotalInterest(principal, annualPercent, months, status) {
    Var years = 0;
    Var interestRate = 0;
    Var totalInterest = 0;
    principal = trim(principal);
    principal = REReplace(principal,"[\$,]","","ALL");
    annualPercent = Replace(annualPercent,"%","","ALL");
    if ((principal LE 0) OR (annualPercent LE 0) OR (months LE 0)) {
        Status.errorMsg = "All values must be greater than 0";
        Return -1;
    }
    interestRate = annualPercent / 100;
    years = months / 12;
    totalInterest = principal*(((1+ interestRate)^years)-1);
    Return DollarFormat(totalInterest);
}
</cfscript>

Reviewing the code

The following table describes the code that has been changed or added to the previous version of this example. For a description of the initial code, see A user-defined function example.

Code

Description

function TotalInterest(principal, annualPercent, months, status)

The function now takes an additional argument, a status structure. Uses a structure for the status variable so that changes that the function makes affect the status structure in the caller.

if ((principal LE 0) OR (annualPercent LE 0) OR (months LE 0)) { Status.errorMsg = "All values must be greater than 0"; Return -1; }

Checks to make sure the principal, percent rate, and duration are all greater than zero.

If any is not, sets the errorMsg key (the only key) in the Status structure to a descriptive string. Also, returns -1 to the caller and exits the function without processing further.

Calling the function

The code that calls the function now looks like the following. Code that is changed from the example in A user-defined function example is in bold.

<cfset status = StructNew()>
<cfset myInterest = TotalInterest(Form.Principal, 
    Form.AnnualPercent,Form.Months, status)>
<cfif myInterest EQ -1>
    <cfoutput>
        ERROR: #status.errorMsg#<br>
    </cfoutput>
<cfelse>
    <cfoutput> 
        Loan amount: #Form.Principal#<br>
        Annual percentage rate:
            #Form.AnnualPercent#<br>
        Loan duration: #Form.Months# months<br>
        TOTAL INTEREST: #myInterest#<br>
    </cfoutput>
</cfif>

Reviewing the code

The following table describes the code that has been changed or added:

Code

Description

<cfset status = StructNew()>

Creates a structure to hold the function status.

<cfset myInterest = TotalInterest (Form.Principal, Form.AnnualPercent, Form.Months, status)>

Calls the function. This time, the function requires four arguments, including the status variable.

<cfif myInterest EQ -1> <cfoutput> ERROR: #status.errorMsg#<br> </cfoutput>

If the function returns -1, there must be an error. Displays the message that the function placed in the status.errorMsg structure key.

<cfelse> <cfoutput> Loan amount: #Form.Principal#<br> Annual percentage rate: #Form.AnnualPercent#<br> Loan duration: #Form.Months# months<br> TOTAL INTEREST: #myInterst#<br> </cfoutput> </cfif>

If the function does not return -1, it returns an interest value. Displays the input values and the function return value.