8.1.13 Declarative Controller Exception Handling

Grails controllers support a simple mechanism for declarative exception handling. If a controller declares a method that accepts a single argument and the argument type is java.lang.Exception or some subclass of java.lang.Exception, that method will be invoked any time an action in that controller throws an exception of that type. See the following example.

grails-app/controllers/demo/DemoController.groovy

  1. package demo
  2. class DemoController {
  3. def someAction() {
  4. // do some work
  5. }
  6. def handleSQLException(SQLException e) {
  7. render 'A SQLException Was Handled'
  8. }
  9. def handleBatchUpdateException(BatchUpdateException e) {
  10. redirect controller: 'logging', action: 'batchProblem'
  11. }
  12. def handleNumberFormatException(NumberFormatException nfe) {
  13. [problemDescription: 'A Number Was Invalid']
  14. }
  15. }

That controller will behave as if it were written something like this…​

grails-app/controllers/demo/DemoController.groovy

  1. package demo
  2. class DemoController {
  3. def someAction() {
  4. try {
  5. // do some work
  6. } catch (BatchUpdateException e) {
  7. return handleBatchUpdateException(e)
  8. } catch (SQLException e) {
  9. return handleSQLException(e)
  10. } catch (NumberFormatException e) {
  11. return handleNumberFormatException(e)
  12. }
  13. }
  14. def handleSQLException(SQLException e) {
  15. render 'A SQLException Was Handled'
  16. }
  17. def handleBatchUpdateException(BatchUpdateException e) {
  18. redirect controller: 'logging', action: 'batchProblem'
  19. }
  20. def handleNumberFormatException(NumberFormatException nfe) {
  21. [problemDescription: 'A Number Was Invalid']
  22. }
  23. }

The exception handler method names can be any valid method name. The name is not what makes the method an exception handler, the Exception argument type is the important part.

The exception handler methods can do anything that a controller action can do including invoking render, redirect, returning a model, etc.

One way to share exception handler methods across multiple controllers is to use inheritance. Exception handler methods are inherited into subclasses so an application could define the exception handlers in an abstract class that multiple controllers extend from. Another way to share exception handler methods across multiple controllers is to use a trait, as shown below…​

src/main/groovy/com/demo/DatabaseExceptionHandler.groovy

  1. package com.demo
  2. trait DatabaseExceptionHandler {
  3. def handleSQLException(SQLException e) {
  4. // handle SQLException
  5. }
  6. def handleBatchUpdateException(BatchUpdateException e) {
  7. // handle BatchUpdateException
  8. }
  9. }

grails-app/controllers/com/demo/DemoController.groovy

  1. package com.demo
  2. class DemoController implements DatabaseExceptionHandler {
  3. // all of the exception handler methods defined
  4. // in DatabaseExceptionHandler will be added to
  5. // this class at compile time
  6. }

Exception handler methods must be present at compile time. Specifically, exception handler methods which are runtime metaprogrammed onto a controller class are not supported.