Can't catch error in service function

59 views
Skip to first unread message

wellercs

unread,
Jan 28, 2012, 12:33:52 AM1/28/12
to framework-one
Sorry for the n00b question I'm about to ask. I tried doing some
Google searching and searching this group's threads but have yet to
find an answer to my issue.

I have a service function where I am intentionally causing an error
because I want to test that I can catch the error from my service
function in my endItem controller function.

I tried earlier to just abort inside the cfcatch in my service
function, but that's not working.

I'm calling index.cfm?action=contactus.submit in the browser.

Below are links to my code.

Controller: http://pastebin.com/LjJiv6sg
Service: http://pastebin.com/tawx96aP

Is there a better way to do what I'm trying to do?

Thank you for your time and attention.

Sean Corfield

unread,
Jan 30, 2012, 4:49:29 PM1/30/12
to framew...@googlegroups.com
On Fri, Jan 27, 2012 at 9:33 PM, wellercs <well...@gmail.com> wrote:
> I have a service function where I am intentionally causing an error
> because I want to test that I can catch the error from my service
> function in my endItem controller function.

Since FW/1 is calling your service, FW/1 will catch your error and
invoke the error handling cycle (normally trying to run the main.error
event).
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Azadi Saryev

unread,
Jan 30, 2012, 7:43:36 PM1/30/12
to framew...@googlegroups.com
what i usually do is set returntype="struct" on all functions that return something. 
in the function i declare a var result = {}; and result.success = true;
then in any cfcatch blocks (and other places, like data validation logic, etc) i set result.success = false;
the function return the result structure (return result;) - the code that invoked the function would first check that result.success was returned true. if it was false that means that there was some error inside the function. i also usually set a result.err variable inside the function and populate it with any error messages i catch - the invoking code can then use this error message to email to me or display to user.

Sean Corfield

unread,
Jan 30, 2012, 8:05:02 PM1/30/12
to framew...@googlegroups.com
On Mon, Jan 30, 2012 at 4:43 PM, Azadi Saryev <azadi....@gmail.com> wrote:
> what i usually do is set returntype="struct" on all functions that return
> something.
> in the function i declare a var result = {}; and result.success = true;
> then in any cfcatch blocks (and other places, like data validation logic,
> etc) i set result.success = false;

There's nothing wrong with a service throwing an exception - if it's
genuinely an exceptional condition and not just an expected "failure"
result. The problem arises if you let FW/1 call services on your
behavior because then you are limiting in how you handle exceptions.
If you explicitly manage and call services yourself - my preference -
then you can wrap calls in try/catch where appropriate. Remember that
exceptions are essentially part of the public API of your service
(because clients need to know about them).

Tom McNeer

unread,
Jan 31, 2012, 8:32:29 AM1/31/12
to framew...@googlegroups.com
Sean,

Just curious --


On Mon, Jan 30, 2012 at 8:05 PM, Sean Corfield <seanco...@gmail.com> wrote:
If you explicitly manage and call services yourself - my preference -
then you can wrap calls in try/catch where appropriate.

... does this suggest that your preferred method is to place the try/catch within your controllers?

Although I've never really seen it discussed, I've always been curious about _where_ in their applications people choose to catch an error and deal with it.

As in, do you catch the exceptions in your service (or maybe even a gateway) and inform other components of the failure up the line, as Azadi does? Or do folks let the exceptions bubble up to, say, the controller, and handle all the aspects (logging, client messages, etc) there?

I know it's an "it depends" thing. Just wondering how you generally handle exceptions.


--
Thanks,

Tom

Tom McNeer
MediumCool
http://www.mediumcool.com
1735 Johnson Road NE
Atlanta, GA 30306
404.589.0560

Sean Corfield

unread,
Jan 31, 2012, 11:45:35 AM1/31/12
to framew...@googlegroups.com
On Tue, Jan 31, 2012 at 5:32 AM, Tom McNeer <tmc...@gmail.com> wrote:
> On Mon, Jan 30, 2012 at 8:05 PM, Sean Corfield <seanco...@gmail.com>
> wrote:
>> If you explicitly manage and call services yourself - my preference -
>> then you can wrap calls in try/catch where appropriate.
> ... does this suggest that your preferred method is to place the try/catch
> within your controllers?

It depends :)

If the service API includes thrown exceptions to indicate specific
(unexpected) failures then, yes, I'd probably put try/catch in the
controller around the service call. But...

A lot depends on how you want to handle unexpected errors. An
exception is meant to be _exceptional_ by definition. If you make a
service call and it fails (unexpectedly) due to a database problem,
what are you likely to be able to do about it? Again, it depends.
Inserting data and getting a data too big for column error indicates a
failure to properly validate input in your application. You should
probably let the default error handling in FW/1 kick in and open a
ticket in your bug tracking system to add proper field validation.
Calling out to a third party service and getting a connection error...
should probably be encapsulated in your service as an "expected"
failure (and either retried or a known, documented failure result
returned to your controller). But there may be reasonable situations
where your controller calls a service that is known to be able to
throw a given exception and your controller could catch that and do
something other than the default error handling behavior.

In other words, because exceptions should be exceptional and
unexpected, the framework's default error handling is likely to be the
most appropriate solution in most cases. But there could be exceptions
(sic) to that "rule" :)

> Although I've never really seen it discussed, I've always been curious about
> _where_ in their applications people choose to catch an error and deal with
> it.

Error is a fluffy term. Exceptions are one thing. Expected failures
are another. They should be handled differently. You should recover
from a failure (because you expect it) but you probably can't recover
from an exception (because it's unexpected).

HTH

Tom McNeer

unread,
Jan 31, 2012, 12:03:23 PM1/31/12
to framew...@googlegroups.com
Thanks Sean,


On Tue, Jan 31, 2012 at 11:45 AM, Sean Corfield <seanco...@gmail.com> wrote:
It depends :)

Maybe we need a new web acronym for that phrase, so we don't all have to keep writing it out.

Although - the natural acronym would be 'id,' which itself brings all sorts of Freudian issues to the already-fraught task of software development ;-)
 
In other words, because exceptions should be exceptional and
unexpected, the framework's default error handling is likely to be the
most appropriate solution in most cases.

Okay. That's pretty much what I thought you'd say, with the obvious need for 'exceptions' to the rule.
 
Error is a fluffy term. Exceptions are one thing. Expected failures
are another. They should be handled differently. You should recover
from a failure (because you expect it) but you probably can't recover
from an exception (because it's unexpected).

And I'll try to be less fluffy in the future.

I used the term 'error' simply because it can (hopefully up until testing is done) include not only 'failures' and 'exceptions,' but also 'stupid mistakes.'

Thanks for being willing to expound a bit on sort of a side issue.

Chris Weller

unread,
Feb 1, 2012, 10:00:00 PM2/1/12
to framew...@googlegroups.com
>Since FW/1 is calling your service, FW/1 will catch your error and
>invoke the error handling cycle (normally trying to run the main.error
>event).

@Sean - Thank you for your quick reply. I removed the try/catch code and the redirect (thinking that may have something to do with it), but it's not falling back to main.error.  When I removed the redirects in the controller, I received an error stating the contactus.submit view was not found, which was expected, so I know the error view is working.  I put the redirects back in, and it's still not providing feedback.  Are you able to produce an error with the code I provided originally?

@everyone-else/@Sean - Thanks for the insightful comments on error handling in controller vs service layer.

Chris Weller

unread,
Feb 1, 2012, 10:18:04 PM2/1/12
to framew...@googlegroups.com
Follow up to my reply:  I put a log statement <cflog text="hi"> in my contactus.cfc submit function in my services directory, and commented out everything else.  I am not seeing the log statement printed in my console.  My form is posting to 'index.cfm?action=contactus.submit'.  It's my understanding that my service in this case would be called by FW/1 directly without me having to code anything in the controller since I have a cfc called contact us and a function called submit.  Hopefully this sheds some light on the issue.

<cfcomponent displayname="ContactUsService" output="false">
<cffunction name="submit" access="remote" output="false" returntype="void">
<cflog text="hi">
</cffunction>
</cfcomponent>

Sean Corfield

unread,
Feb 1, 2012, 11:06:30 PM2/1/12
to framew...@googlegroups.com
Which version of FW/1 and what is suppressImplicitService set to?

In 1.x, FW/1 called a service automatically but 2.0 deliberately does
not. That setting lets you have 1.x behavior in 2.0.

Most people outgrow the implicit service calls pretty quickly and it
was causing a lot of confusion, hence the change...

Sean

Chris Weller

unread,
Feb 1, 2012, 11:52:19 PM2/1/12
to framew...@googlegroups.com
@Sean

>Which version of FW/1 and what is suppressImplicitService set to?

I am running 2.0.0 with the default settings, so suppressImplicitService was set to true!  I now see this on the wiki.  I apologize for not reading that well enough.  I changed my code to EX-plicitly call the service, and it worked.

"Prior to FW/1 2.0, a service method was automatically called, with a name that matched the action and the result was placed in rc.data. FW/1 1.2 introduced a configuration variable to control this behavior and that variable is still present in 2.0, but the default behavior has changed so that service methods are not called automatically."

>In 1.x, FW/1 called a service automatically but 2.0 deliberately does
>not. That setting lets you have 1.x behavior in 2.0.

Now that you have enlightened me, I like the fact that 2.0 suppresses the implicit service call.  I like to have control over this and not worry about shooting myself in the foot by adding a service function at a later date that could affect other requests adversely.

Thanks again for your patience and prompt responses.

Sean Corfield

unread,
Feb 2, 2012, 12:33:24 AM2/2/12
to framew...@googlegroups.com
On Wed, Feb 1, 2012 at 8:52 PM, Chris Weller <well...@gmail.com> wrote:
> Now that you have enlightened me, I like the fact that 2.0 suppresses the
> implicit service call.  I like to have control over this and not worry about
> shooting myself in the foot by adding a service function at a later date
> that could affect other requests adversely.

Cool! With DI/1 it's very easy to manage your model / services and
have FW/1 & DI/1 collaborate to autowire everything.

Now that FW/1 2.0 is gold, my goal is to have DI/1 hit 1.0 fairly
soon. Then I can go back to cfmljure and get that into a more
user-friendly format (not that everyone is going to be interested in
using Clojure with CFML :)

Mark Drew

unread,
Jan 31, 2012, 9:38:23 AM1/31/12
to framew...@googlegroups.com
A controller does seem to be the best place for them since you want to pass it along to the view. 

For example:

UserService.save() throws UnknownUserException (or whatever you call it) 

The controller needs to know about that so it can do :

rs.error  = "";

try {
UserService.save(user);
}
Catch(Any Exception){
rs.error  = Exception.message;
}



and then in your view you can do:

<cfif Len(rs.error)>
Oh noes! your user wasn't saved because:
<p>#rs.error#</p>

</cfif>


But those are exceptions that are trapped, but if it is an exception that (for example) the database is down, that could be a general exception, so now, instead of of just throwing it we could re-throw it. 

try {
UserService.save(user);
}
Catch(Any Exception){

if(NOT ListFind(HandledExceptions, Exception.type)){
rethrow(Exception) ;
}
rs.error  = Exception.message;

}




(don't try this code at home, I just wrote it off the top of my head) 

MD 

--
FW/1 on RIAForge: http://fw1.riaforge.org/
 
FW/1 on github: http://github.com/seancorfield/fw1
 
FW/1 on Google Groups: http://groups.google.com/group/framework-one

Chris Weller

unread,
Feb 10, 2012, 9:11:31 PM2/10/12
to framew...@googlegroups.com
@Mark - thanks for the advice! I agree with your approach.
Reply all
Reply to author
Forward
0 new messages