13 Replies Latest reply on Sep 1, 2009 10:59 AM by Jens Wegar

    Data services error handling - best practices?

    Jens Wegar Level 1

      Suppose I have a Authentication service set up using PHP and Zend_amf. I realize that problems related to connecting to the service can be handled by a faultHandler. But what I'm wondering is what would be the best way to handle errors where e.g. the service connected just fine, but user credentials were wrong? The way I see it there are two ways:

       

      1. throw a new Exception on the PHP side. In this case the faultHandler of the RemoteObject would catch it.

      2. return a String or an error object from the service, which would indicate the error type. In this case the resultHandler of the RemoteObject would have to a) accept a result of type Object as the return type and b) parse the result to see if there was a error or everything was fine.

       

      #1 seems reasonable, however the message that gets sent back is perhaps unecessarily verbose (giving class paths etc). Perhaps there is a way to decide how much information should be passed into the exception, I don't know yet.

      #2 would also work, but there I think the problem would be more of a logical kind. I feel that errors, regardless of their nature, should be handled in one function and successfull results in another. Besides, one would miss out on strong typing the result I think?

       

      Personally, I don't have that much experience of RemoteServices, so I would love to hear anyone's thoughts on the matter. Are there any best practices for this available?

        • 1. Re: Data services error handling - best practices?
          cwrindfuss

          I've come to the realization that there are two different situations at play when dealing with remote objects. First the channel or remote server can fail meaning that it is impossible for a reasonable response to be returned. This is what I think of as a "fault". There is nothing that the end user can do about this situation and it seems to make sense that a single bit of code in the client catch and handle the fault.

           

          The second situation is one where the remote call worked successfully. A call was made from the client to server, the remote service did its thing, preparing a return object and sending it successfully to the client. No fault happened. In this case there is meaningful information to display to the user and the user interface should expect that the user will interact with that information.

           

          In the second situation the result may be what the user expected--the happy path--or it may be a "you can't do that" error condition. All successful responses result in planned and designed workflows. In the case of an error, the workflow takes the user through the steps needed to correct the situation. For the happy path response the user is taken to the next logical step. As the error takes the user through a correction workflow it is next to impossible to handle all errors in a single routine on the client side.

           

          Based on the above, my practices have been to develop a common fault handler that can catch faults from any remote call and let the user know there is a major problem. For operation errors, they are handled like any other successful response. The return object is in the form of a wrapper containing an error code, error message, and the happy path data object. This does mean that response code needs to be written to check the error code and pick an alternate workflow when an error is indicated.

           

          One last thing. The application I'm working makes lots of remote calls. What we have found is very few true "errors" (not faults) can occur. Careful design of the UI can eliminate most errors. In one development project we started out building our return object wrapper. By the time all of the code had been implement we found there was not a single "error" that would occur. We removed the wrapper in this situation.

           

          Hope this helps.

          1 person found this helpful
          • 2. Re: Data services error handling - best practices?
            Tomislav Pokrajcic

            Using return wrapper is usually the best way to go.

            Generally, server requests can be divided in 2 types:

            - request for data

            - command

             

            Request for data can further be split in 2 subtypes:

            - expected return is array/collection of objects

            - expected return is a single value ('get object by providing id' kind of situations)

             

            Command requests are those that demand an action on server side (login, create user, delete user, sendEmail...).

            Sometimes they return data (login can return UserVO) and sometimes not (delete for example).

            However commands always return some status. I usually set error statuses as negative integers and happy statuses as positive integers and use that for branching in resultHandler.

            It's true that very small percentage of server functions can return more than one happy status but sometimes it's nice to have that option.

             

            Usually wrapping just command requests is enough. If data request fails, detecting null can be enough to decide what next...

             

            class CommandStatus {

                 var status:Number;

                 var description:String:

                 var data:*;

            }

            1 person found this helpful
            • 3. Re: Data services error handling - best practices?
              Jens Wegar Level 1

              Thanks for your reply, cwrindfuss!

               

              One question, when you do your happy path scenario the way you described it, doesn't that mean that you cannot do strong typing on the resultObject that is returned from the service? If I understood you correctly you would e.g. return a UserProfile object if all went well, and a ErrorWrapper object if something went wrong and then check which actually happened in the resultHandler? And that means your resultHandler would have to accept any type of object?

               

              I understand however the logic in having fault handlers called only when something is seriously wrong. But I feel that both paths sort of will duplicate functionality. Consider for instance an AIR application that needs to connect to a server to fetch something. In case of a successfull call, the app would change the text in a label component. But if no network connection is present (which is something the faultHandler would catch eventually), I don't think I'd want to send the user into the serious-fault-path. Instead I might want to use the same label component to inform the user of the error, since this is a fault that the user might be able to rectify by connecting a network cable. But then you have the scenario where e.g. a PHP script throws an error, which probably is something the user cannot do anything about. This would also be caught by that same faultHandler, but since this is a serious error I would want to throw a popup dialog at the user with more detailed information about the error. So to handle this, I would have to have logic in both the faultHandler and resultHandler to direct the flow to either a happy-path(resultHandler) / serious-fault-path(faultHandler), or plain error-path (both), right?

               

              Hope I'm making myself understandable, my brain is currently going on overdrive trying to make sense of this

              • 4. Re: Data services error handling - best practices?
                Jens Wegar Level 1

                Thanks, Tomislav!

                 

                So basically, you always wrap all you results in the Wrapper containing those properties, with the data property containing the actual result (be it a single object or an array of objects) ?

                 

                Maybe too soon to tell, but so far it would seem that leaving the faultHandler to deal with serious errors only is the preferred way to go.

                • 5. Re: Data services error handling - best practices?
                  Tomislav Pokrajcic Level 1

                  In my current project I wrap only 'command' calls.

                  I leave getCustomers and getAccountInfo kind of methods unwrapped since I find it enough to detect if the result is null or value to decide what to do next with GUI.

                  For example, if getCustomers doesn't find any customers for given filter I'll return an empty Array.

                  If database error occurs (that I succesfully handled with try...catch) I return null. That way I always know on client if I have a valid response.

                   

                  I talked to several people recently about various practices for handling this part of application and most use wrappers in some way.

                  Some people wrap everything that's wrappable, and some just chosen methods...

                   

                  Generally, if something serious must be handled somewhere up the call stack - use exception. Otherwise error codes provide good enough mechanism for code branching...

                   

                  Cheers,

                   

                  Tomislav

                  • 6. Re: Data services error handling - best practices?
                    cwrindfuss Level 1

                    Jens, I fully understand trying to get your head around this issue.

                     

                    The real question that needs to be understood is what do you expect the user to do if the data isn't returned. In your AIR application example, is the UI still usable (the user can still do work) if the label data isn't returned? If the answer is yes, then the wrapper with an error code is the appropriate way to return the data. If the UI is unusable, then a fault would be appropriate. Let's assume that the UI remains useable without the label. The UI code doesn't care why the label wasn't returned, it just doesn't have data to display. I would just ignore a fault or error and put a default "data not available" message in the label for the user. If the requirement is to tell the user why the data isn't available, then you will have to have logic on both the fault and error sides to create the appropriate label value.

                     

                    We are able to strongly type our returning objects. To do this we define custom wrapper objects that all extend a base class. The wrappers all have error code, and a strongly typed object that contains the desired data. Takes more work to define the objects, but we wanted strong typing.

                    • 7. Re: Data services error handling - best practices?
                      Jens Wegar Level 1

                      Guys, thanks a lot to both of you for your answers!

                       

                      You both truly helped me wrap my head around this issue. I will go with the route of wrapping all my result objects and leaving the fault handler to handle system critical errors. The comment about "is the UI still usable" kind of nailed it for me. Also, very true that there are few cases where checking for null wouldn't be enough to determine whether or not a call was successfull.

                      • 8. Re: Data services error handling - best practices?
                        SunilAdobe Adobe Employee

                        Hi Jens,

                         

                        Can you try using the "connect to data/service" menu option under the data menu to connect to your PHP backend. A service created like this provides you good wrappers for invoking your service, error handling and a host of other features, as described in the following links.

                         

                        http://livedocs.adobe.com/flex/gumbo/html/WSbde04e3d3e6474c4-668f02f4120d422cf08-7ffa.html

                        http://sujitreddyg.wordpress.com/2009/06/01/building-a-database-based-app-using-flex-and-p hp-with-flash-builder-4/

                         

                        Hope this helps.

                        -Sunil

                        • 9. Re: Data services error handling - best practices?
                          Jens Wegar Level 1

                          Hi Sunil,

                           

                          I've used those methods to generate my services, however as far as I can see they do not actually provide any standardized ways of handling errors as I described in this post? They do not answer the issue of "if the service responded ok, i.e. no server error occured which would be caught by the faultHandler, but the result was an error (e.g. user authentication failed), how do you check for and branch according to these error situations?" Event with the generated code, you still need to decide yourself what you want to have happen once a result, error or fault comes back from the server, and it's this logic that I was having trouble with.

                           

                          I have now ended up with a setup where I have an AbstractResponse and AbstracRequest class, which act as base classes for the traffic between my client and the server (since there is some basic data that I need to transfer on each call regardless of service). All objects that I send over the wire extend one of these two classes, depending on the direction (client->server or server->client). The AbstractResponse class contains properties for status and errorMessage. Status = 1 indicates a success, -1 indicates error (not a fault event). These I check on the client side and branch accordingly within my resultHandler. The faultHandler works as usual, i.e. catches "serious" errors within the service (e.g. no server connection).

                           

                          Do you have any suggestions regarding this? I would of course love to hear Adobe's opinion on the matter, since you guys after all should be the experts on the subject.

                          • 10. Re: Data services error handling - best practices?
                            SunilAdobe Adobe Employee

                            Hi Jens,

                             

                            There are 3 ways to handle this, the first is one is the that you already are following. But the recommended approach is that, if you think that your server call is indeed should return an error then it should.

                             

                            For example, If my service is a login operation and takes user id and password and it returns null as there is no record for that particular user id, then the following are the other 2 ways

                             

                            2. Return null and the call will be successful, handle that in your result handler to check whether the returned value is correct, if not proceed to the fault handler

                            3. You can actually thrown an exception (based on what your backend provides, which is kind of a generic wrapper class) and that will come as fault on your client side.

                             

                            Let us know if this helps.

                             

                            PS: If you are using BlazeDS, it will automatically serialize the exceptions in AMF and it should be easily accessible in your fault handler, check out the following blog post http://sujitreddyg.wordpress.com/2008/02/12/handling-java-exceptions-in-flex-application/

                             

                            If you are using PHP, Exception class will do the same and if you are using CF, CF has inbuilt mechanisms to serialize errors to appropriate AMF exceptions which could be accessed in your fault handlers.

                             

                            Thanks

                            -Sunil

                            • 11. Re: Data services error handling - best practices?
                              Jens Wegar Level 1

                              Hi Sunil,

                               

                              thanks for your reply. So Adobe basically recommends that all errors, regardless of their type should be handled in the faultHandlers, and only the all is well happy path should be in the resultHandler?

                               

                              I considered #2 and #3 earlier also, and my first thought was indeed to throw an Exception also for e.g. invalid credentials. I'm using PHP and Zend_AMF for the server side code and I noticed exceptions came down the pipe nicely. There however my concern is that there is a lot of information passed back to the client with that exception that I don't necessarily want transferred over the wire. It's the faultDetail property of the flex.messaging.messages.ErrorMessage that I would like to not have transferred over the wire. For instance, the complete stack trace which indicates where in the PHP code the exception was thrown (including server file paths(!), class and function names, line numbers) is in this property. This is of course good during development when there might be accidental causes for this, but in a production environment I wouldn't want this to go over the wire. Is there a way to prevent this? I've tried extending the default Exception class and setting various variables to empty strings, but that doesn't help because it seems the items that really need to be overridden in PHP are marked final and thus cannot be overridden. Or am I missing something?

                               

                              As for suggestion #2, yes I could return a null value, but then I would have no way of letting the client know why the service returned null, other than if I wrap the return in an object the way I now do (in which case I'm back at #1)? In case of the login example, yes the most probable reason is that the user entered the wrong credentials. But it could also be because the web service could not connect to the database server, or because the service is down for maintenance, or other situations that cannot be branched at the client side by simply returning null.

                               

                              So basically I'm back to battling between #1 and #3... Any thoughts?

                              • 12. Re: Data services error handling - best practices?
                                SunilAdobe Adobe Employee

                                Hi Jens

                                 

                                In dev environment in the amf_config.ini file, the amf.production is set to false by default and you get these extensive error messages. If you set the amf.production = true in amf_config.ini file then the stack trace and the error message too will not be sent to the server.

                                 

                                However, If you want only the message in the Exception to be available in FaultEvent.fault.faultString make the following modifications to _errorMessage() function ZendFramework\library\Zend\Amf\Server.php in ZendAMF code. If you observe the error details is sent only if amf.production = false.

                                 

                                    protected function _errorMessage($objectEncoding, $message, $description, $detail, $code, $line)

                                    {

                                        $return = null;

                                          switch ($objectEncoding) {

                                               case Zend_Amf_Constants::AMF0_OBJECT_ENCODING :

                                                    return array (

                                                              'description' => $description,

                                                              'detail' => ($this->isProduction ()) ? '' : $detail,

                                                              'line' => ($this->isProduction ()) ? 0 : $line,

                                                              'code' => $code

                                                   );

                                               case Zend_Amf_Constants::AMF3_OBJECT_ENCODING :

                                                    require_once 'Zend/Amf/Value/Messaging/ErrorMessage.php';

                                                    $return = new Zend_Amf_Value_Messaging_ErrorMessage ( $message );

                                                    $return->faultString = $description;

                                                    $return->faultCode = $code;

                                                    $return->faultDetail = $this->isProduction () ? '' : $detail;

                                                    break;

                                          }

                                        return $return;

                                    }

                                 

                                 

                                Hope this helps.

                                • 13. Re: Data services error handling - best practices?
                                  Jens Wegar Level 1

                                  Thanks Sunil, I will try this.