Grails Custom Exception Handling

Updated On: 2 Jul 2008 - Tested on Grails 1.0.1
Grails allows you to perform custom , fine-grained Exception handling for error conditions in your controllers. As per usual, you can take advantage of this feature by making use of Grails' nifty Dependency Injection features. In this case, we will depend on the GrailsExceptionResolver class being injected into /conf/BootStrap.groovy as "exceptionHandler".

Note: This article shows you a fine-grained alternative to the "500 Internal Server" catch-all which is generated by default in /conf/UrlMappings.groovy

The important changes are in the below bit of code on lines 3, and 7 to 9 of my BootStrap.groovy file:

   1:class BootStrap {
2:
3: def exceptionHandler
4:
5: def init = { servletContext ->
6:
7: exceptionHandler.exceptionMappings =
8: [ 'NoSuchFlowExecutionException' :'/my/doIt',
9: 'java.lang.Exception' : '/error']
10:
11: }
12:
13: def destroy = { }
14:}

In the above code snippet we set the exceptionHandler's exceptionMappings property to a Map containing the name of the exception we wish to handle, mapped to the URI we wish to handle our error condition.
Line 9 above is very important. This is our "catchall" for all other java.lang.Exceptions to be handled by Grails' internal error handler. It is worth noting that leaving out "java.lang" will have the incorrect bevaviour. Taken from the javadoc for GrailsExceptionResolver's setExceptionMappings() method:


NB: Consider carefully how specific the pattern is, and whether to include package information (which isn't mandatory). For example, "Exception" will match nearly anything, and will probably hide other rules. "java.lang.Exception" would be correct if "Exception" was meant to define a rule for all checked exceptions. With more unusual exception names such as "BaseBusinessException" there's no need to use a FQN.


NOTE: In the above code the URI maps onto our urlMappings, therefore
  • '/my/doIt' - invokes doIt closure on MyController
  • '/error' - skips straight to /views/error.gsp

Lastly, in order for all the above to work, we must remove the default "500" error handler from your /conf/UrlMappings.groovy as follows (line 8):
   1:class UrlMappings {
2: static mappings = {
3: "/$controller/$action?/$id?"{
4: constraints {
5: // apply constraints here
6: }
7: }
8:// "500"(view: null)
9: }
10:}
References:

14 comments:

Mike Hugo said...

Thanks for this - I was just trying to figure out how to this - now I have nice error handling for NoFlow exceptions!

Rintcius said...

Hi Stephan,
This looks interesting. However, I cannot get it to call the controller closure. Does this closure need to have a special format or something?

if I add to the Bootstrap:
exceptionHandler.exceptionMappings =
[ 'RuntimeException' :'/internal/handleException',
'Exception' :'/internal/handleException' ]

and define in InternalController:

def handleException = {
println(params)
render(text:params, contentType:"text/plain")
}

I get this result:
HTTP ERROR: 404
/WEB-INF/grails-app/views/internal/handleException.jsp

So it looks like it cannot find the controller closure (or it doesn't even try to find it and tries to jump straight to the view)

Any ideas?

Stephan February said...

Hi rintcius

It looks like your handler is working fine.
Did you check and make sure that you actually have a .gsp called

/WEB-INF/grails-app/views/internal/handleException.gsp

Cheers
Stephan

Rintcius said...

Thanks for your response Stephan.

Yes, the exceptionHandler does work to the extent that if I add a gsp then it finds and returns that.

What I was referring to is that you mentioned that it can call the controller closure which I cannot get working, since it always seems to jump straight to the view and ignores the controller (that's why I called render with the text parameter so that it would return the text directly instead of something in the view)

shyam kishore said...

The exception handler doesn't call the controller closure from the URL mappings instead it is looking for the gsp

Rintcius said...

Thanks for your message, Shyam. I also thought it did, so your confirmation is helpful.

doyle said...

So you can see it in development or have a custom one to have your testers report what's up with your bad code.

have some grails stuff on blog.doylecentral.com



import grails.util.GrailsUtil

class BootStrap {
def exceptionHandler

def init = { servletContext ->

switch (GrailsUtil.environment)
{

break
case "test":
exceptionHandler.exceptionMappings =
[ 'NoSuchFlowExecutionException' :'/gas/error',
'Exception' : '/error']
break
case "production":
exceptionHandler.exceptionMappings =
[ 'NoSuchFlowExecutionException' :'/gas/error',
'Exception' : '/error']
break
}
}

Brock Heinz said...

Hey there Stephan,

Did you ever test what you described?

> '/my/doIt' - invokes doIt
> closure on MyController

If you did test it - which version of Grails were you using? I've tried 1.0.3 with no avail. The problem could be related to the bug I've recently reported.

I was never able to invoke the controller directly. As a work around, I configured a '500' handler in my UrlMappings file, and then detected the type of exception that caused the error. If the 500 was due to a 'NoSuchFlowExecutionException' I'd then redirect to a GSP from within the flow. More details on that here.

Brock

Stephan February said...

Hello Brock

I've updated the code snippet. It seems that the behaviour for GrailsExceptionResolver has changed subtly so that one now has to fully qualify the "java.lang.Exception" catchall. Failing to do the above qualification will lead to the "catchall" always catching any Exceptions ending in *Exception

Stephan

Michu said...

How do I access the actual exception in /my/doIt?
Thanks

Tom said...

Hi Stephan,

Have you tried this exception handling with Grails 1.1 and web flow?

It seems like the behavior has changed in 1.1. When the flow or session expires, I always seem to get redirected to a new flow and no exception is thrown.

thanks,
tom

Stephan February said...

Hi Tom

Nope, have not tried with the latest version. Thanks for the heads-up. I'll have a look at it.

Cheers
Stephan

AddictedToCoffee said...

Hi Stephen,

This handling doesnt seem to work. It doesnt actually call an action in the controller, it just looks for a view within the views directory.

Is there anyway to actually call a controller action using this type of handleing?

Thanks

Christian

Sam's Blog said...

I think this is the simplest solution:

1. Add a mapping entry to UrlMappings.groovy

'500':(view:'error')

2. Under grails-app/views, create a file named error.gsp, in which you write your custom error msg

Post a Comment