Grails and Error Handling
I finished writing my first plugin today. As with most things Grails, it was surprisingly easy. The plugin injects some methods into my domain and controller classes which I can use to streamline my code.
I am not too fond of using if statements to direct flow around error handling code. For me, it clouds the core logic of a piece of code. If the normal flow of the code is assumed to execute error free, why shouldn't the code look like it does?
The idea is very simple. I inject some new methods that will throw exceptions on failure. I've called them trySave, tryDelete and tryValidate. Consider this code, generated kindly by Grails:
def save = {
def jsecUser = new JsecUser(params)
if(!jsecUser.hasErrors() && jsecUser.save()) {
flash.message = "JsecUser ${jsecUser.id} created"
redirect(action:show,id:jsecUser.id)
}
else {
render(view:'create',model:[jsecUser:jsecUser])
}
}
After installing my plugin, I can do this:
def save = {
withErrorHandling {
try {
def jsecUser = new JsecUser(params)
jsecUser.trySave()
render(view:'create',model:[jsecUser:jsecUser])
} catch (ValidationException e) {
flash.message = "JsecUser ${jsecUser.id} created"
redirect(action:show,id:jsecUser.id)
}
}
}
And if I define a closure named errorHandler in my controller, I can add custom error handling for all actions in this controller:
def errorHandler = { ex ->
if (ex.class == StoreException) {
// log this and redirect to somewhere useful to the user, or perhaps return to the 'edit' page if there is an 'id' parameter.
} else {
defaultErrorHandler()
}
}
Ideally, I would like to wrap the controller actions in a try..catch block and not have the extra withErrorHandling syntax, though I am not sure how to go about this yet. Any pointers anyone?
All in all, I rather like not dealing with the save method returning null on failure, and also catching those annoying constraint exceptions, as the try* methods are called with flush:true by default. Now it's time to use this a little and see how I like it in practice - I can already see I've over-designed this simple API as I can't think of a use case for tryValidate :)