Error handling is one of the most often overlooked parts of application development. How often have you been to a web page and seen a database error, JavaScript error, connection error, or page not found error? These are examples of errors that are not handled properly. It is up to you to decide how you want to recover from a particular error, but I will show you where the errors can occur in a Flash Remoting application and give you some basic strategies for developing your own error handling. 4.6.1 Error TypesErrors can occur in any aspect of your Flash movie or in the server-side code of a Flash Remoting application. Errors can be broken down into these basic types:
Although the errors themselves can't be eliminated, by adding validation code inside your Flash movie you can recover from the inevitable errors gracefully. Many errors are caused by users using your application in ways that you hadn't anticipated. Perform extensive beta testing and add code to prevent or gracefully handle the errors you discover. An ounce of prevention is worth a pound of cure when it comes to user input. For example, restricting the length and allowed characters in a user input field can eliminate most errors and make it simpler to handle those that occur. External data, whether coming from user input or a remote server, is a common source of error. That said, server errors are handled quite nicely by Flash Remoting and most of them can be trapped easily inside of your Flash movie. 4.6.2 Trapping Server-Side ErrorsAn error that is detected is said to be trapped . Once an error is trapped, you usually need to handle it in some useful way. For example, if the server is inaccessible, you might automatically try again in several seconds, or you might display a message explaining the problem to the user. Server errors are passed to the Flash movie in the serialized onStatus event of a remote call, which triggers an onStatus event in the Flash movie, which in turn is handled by the onStatus( ) method of the responder object or the methodName _Status( ) function, as described earlier. A status object , which you can use to understand and handle the error, is passed to the error handler (either onStatus( ) or methodName _Status( ) ). In previous examples, I displayed the description property of the status object, which contains a human-readable error message, in the Output window or a text field. The complete properties of the status object are shown in Table 4-1. Table 4-1. Properties of the status object
Most errors in services should be captured at the server level. That is, you can write your CFML, C#, Java, or other server-side code to detect certain types of errors. After capturing the error on the server, you must decide how to handle the particular error:
Of course, you can leave the error unhandled or even untrapped on the server, which would leave the error trapping and handling up to the Flash movie entirely. Abdicating control over errors that can and should be trapped or handled at the server level is a poor approach because it leads to errors that can't be prevented or detected on the client side. Of the various options, the most appealing is to trap the error on the server and send a controlled response to the client where specific and appropriate action can be taken. This approach keeps your error-trapping code on the server separate from your responder code on the client. Typically, you would have a try/catch block on the server to capture the error and then use a throw statement to create your own error message to pass back to Flash. This way, you can control how the error appears to the Flash movie. The ColdFusion code in Example 4-4 demonstrates . Example 4-4. Capturing errors on the server with testError.cfc<cfcomponent> <cffunction name="myFunction" access="remote" returntype="string"> <cfargument name="myArgument" type="string" required="true"> <cftry> <cfquery name="myQuery" datasource="northwind"> SELECT * FROM Customers WHERE CustomerID = '#myArgument#' </cfquery> <cfcatch type="database"> <cfthrow message="Database error"> </cfcatch> <cfcatch type="any"> <cfthrow message="Other error"> </cfcatch> </cftry> <cfif myQuery.recordcount EQ 0> <cfthrow message="User not defined"> </cfif> <cfreturn myQuery> </cffunction> </cfcomponent> The corresponding ActionScript code is shown in Example 4-5. Example 4-5. Responding to server errors in Flash with testError.fla#include "NetServices.as" var my_conn; // Connection object var my_service; // Service object // Responder for general service methods var Responder = new Object( ); var myURL = "http://localhost/flashservices/gateway" // Capture connection errors and attempt connection up to 5 times Responder.onResult = function (myResults) { trace("No errors"); // put responder code here }; // General error-handling routing Responder.onStatus = function (theError) { switch (theError.description) { case("HTTP: Failed"): trace("Connection error."); // In actual practice, send the user to a general error page break; case("Service threw an exception during method invocation: Database error"): trace("There was a database error"); break; case("Service threw an exception during method invocation: Other error"): trace("There was an undefined error"); break; case("Service threw an exception during method invocation: User not defined"): trace("The user was not found in the database"); break; default: trace("There was a general error"); } trace(theError.description); }; // General connection errors or other errors use the Responder object as well System.onStatus = Responder.onStatus; function init( ) { NetServices.setDefaultGatewayUrl(myURL); my_conn = NetServices.createGatewayConnection( ); myService = my_conn.getService("com.oreilly.frdg.testError"); } // Start init( ); myService.myFunction(Responder,"test"); As you can see from the onStatus( ) function, the error messages returned by the .cfc method are trapped in the switch statement. You should handle the errors as you see fit, such as by redirecting the user to a general error page where you display information about the error. |