I have recently run across a general problem , and it's about conveniences in the application when its talked about errors, exceptions, status codes etc.
1. request is made, but the session is timed out and user is offline.
what should be returned ? exception ? error ? some special status code ?
2. user submitted information that he have no permission to. again - exception ? error ? status? and if the permission-validation is on field-level, and is part of the validations ?
3. ext forms have a standard of 'errors' in the responce, when validation failed. why ? it's stated over the internet that :
Error: Any departure from the expected behavior of the system or program, which stops the working of the system is an error.
Exception:Any error or problem which one can handle and continue to work normally.
doesn't an invalid input is a something one can handle (change..) and then resubmit?
4. some apps(non-web) iv'e seen use 'error codes' - maybe thats the solution ? e.g throwing errors in format 'errorcode:errordescription' , and then analyzing the error code on client-size ( which also gives place for multilanguage error-descriptions ) ?
another issue is with the 'success' property - where it comes inside all that ?
This mess is really driving me crazy and i'm determined to make some cleanup as part of a big refactoring and migration to ext 3. also, a web-service-api is going to be made, so this conveniences doubled in importance.
I probably didn't explained my problem well , as i'm very confused about all the subject.
my problem is finding a good approach for robust application. there are many peaces here and i don't know how to handle them "right" : exceptions, errors, status codes, various success indicators, error-codes etc etc. that makes a big mess in my head.
Since posting this thread, i have already made up some possible "protocol" :
* 'success' , 'errors' , 'warnings' etc properties are just a legitimate part of a "successfully" returned data. not more,not less. so each client-side / api-caller handles them internally, based on the called method specs.
* exceptions are thrown in backend for every case that is not "successfully returned data". i.e session timeouts, database falls, unpermitted access, backend library errors, failed assertions etc etc. in front-end, they are handled on an application-level ( not method ).
They are also logged ( in contrast to 'errors' ).
* just like in Ext.Direct , 'type' is returned, and can be only 'rpc' or 'exception', when exception adds additional 'message' property.
* exception 'message' always in the format: "identifier::message". there are too many reasons to use exception identifiers ( multilanguage messages, logging, client-side exception handling etc ).
This is just a draft, and i would very like to get any corrections/ advices/ links etc.
I've standardized on the following convention:
- JSON-RPC protocol, to avoid too much special-cased logic when calling the server. Server-side there's a base class that all web services use, making it easy to extend the generic implementation.
- JSON-RPC faults are used to handle application-level exceptions (timeouts, missing rights) not explicitly handled server-side by the particular implementation of the web service.
- The client-side JSON-RPC request method detects whether the return value is a JSON-RPC fault or a result. If it's a fault it calls the "failure" handler passed into the method, otherwise it calls the "success" handler. If the "failure" handler does not exist or does not return true, it shows an error message. This means there is no possible situation in which the user does not get appropriate error treatment. At the very least they'll get an error message explaining what's wrong.
- The "success" handler receives the result object. In the case of validation messages, this might be an object containing the validation errors. For me this is not an exception, this is simply an expected result.
thanks @joeri . It tells me i'm on the right route ( or at least not alone )
The point with the method error handlers which return return true when error is handled - thats really useful.. thought i'm not a fan of "success" and "failure" methods.. i always use "callback". maybe it's time to change habits .
May i ask you how do you distinguish between exceptions ? for example, some exceptions throws user away, while others may be completely ignored . raw message analysis, or some exception codes/constants ? i thought about "errorName::errorDescription" format, but maybe there's something i missed here .. ?
I throw errors whenever the back end discover has any issue at all, including the ones covered in these posts.
I wedge into Ext.Ajax.requestComplete() and check the incoming JSON for error and throw up a dialog. Calling the request's failure() function allows the code doing the request to clean up properly - such as re-enabling submit buttons and that sort of thing.
Since my server side code is PHP, my requestComplete() handler also detects PHP errors in the response and throws up a dialog for those as well.
It's only a little tricky when Ext's Store logic tries to load a store and there's an error. I'll leave that to you to figure out.
As far as exceptions, I make the simple distinction that any unhandled PHP exception is a fault condition that triggers the failure handler. There's a generic try / catch block in the web service base class (I use a subclassed Zend Framework's Zend_Json_Server). It's up to the implementation of any particular web service method to catch exceptions if it expects to trigger them. Expected errors are therefore also just regular result values. I'm not a fan of using exceptions as "secondary return values", because it makes it more difficult to reason about program behavior. In the client-side code, any "unrecognized" (non-json) result will also trigger the failure handler.
The reason for separating the success and failure handlers is because I work together with other developers who don't necessarily have the same framework comprehension I have. By keeping the handlers single-purpose, there's no confusion possible about what they're supposed to do. In practice I find the code more readable. It's pretty rare that there's much shared code between a success and a failure condition, so having two separate functions instead of an if/else structure tends to be cleaner.