Controller Exception Handling Custom exception handling at the controller level

Controller Exception Handling

Hanami automatically handles exceptions for you. The default behaviour in production is to catch all exceptions and render an error HTML page. In the development and test environment exceptions are not caught by the framework’s exception handling, instead you’ll be presented with a stack trace for debugging.

You can define this behaviour in the apps/[your_app_name]/application.rb file, just look for the handle_exceptions setting in the environment specific configuration blocks and uncomment it. The comments reflect the default setting, i.e., if you want to change the behaviour, you’ll need to change the setting from true to false or vice versa depending on the environment block.

# apps/web/application.rb

module Web
  class Application < Hanami::Application
    # ...

    # Handle exceptions with HTTP statuses
    handle_exceptions true

    configure :test do
      # Don't handle exceptions in tests, render the stack trace
      handle_exceptions false
    end

    configure :development do
      # Don't handle exceptions in development, render the stack trace
      handle_exceptions false
    end

    # ...
  end
end

The default behaviour is fine if you are rendering HTML pages, however, for APIs you should always return responses in the respective format, e.g., JSON or XML.

Let’s assume you are implementing a JSON API for managing a Library. Your current task is to implement a checkout procedure and you need to decide what happens when two patrons try to borrow the last available copy of a book. Thanks to ACID guarantees of your database you are sure that when you book the last copy for one patron, the other will fail to book it. However, you need to gracefully handle this scenario.

Your controller could look something like this:

# apps/inventory_api/controllers/loans/create.rb
module InventoryApi
  module Controllers
    module Loans
      class Create
        include InventorApi::Action

        handle_exception Inventory::OutOfStock => :borrowing_failed
        expose :loan
        
        params do
          required(:book_id).filled
          required(:patron_id).filled
        end

        def call(params)
          patron = PatronRepository.new.find(params[:patron_id])
          book = BookRepository.new.find(params[:book_id])
          # calling borrow may raise Inventory::OutOfStock
          @loan = Inventory.borrow(book, patron)
        end

        def borrowing_failed(_exception)
          error = {
            status: "not_found",
            code: "out_of_stock",
            title: "No more copies of the requested book are available",
          }

          halt 404, JSON.dump(errors: [error])
        end
      end
    end
  end
end

By using handle_exception Inventory::OutOfStock => :borrowing_failed we tell Hanami to call #borrowing_failed when the Inventory::OutOfStock exception is raised. The #borrowing_failed method is then handed the exception object for further inspection, but we don’t actually need that object in our example. Instead we simply render a JSON:API error response to tell the client that no copy is left for borrowing.

Since we wish to document that this actually works, we write a test for this behaviour. However, when we run our test the expected JSON response is not rendered, instead we are presented with a stack trace.

We remeber that this is due to Hanami’s default behaviour in face of exceptions. For our test to succeed we need to change how Hanami handles exceptions in the test environment for this controller. We could change it for all controllers of this application with the following configuration:

# apps/inventory_api/application.rb

module InventoryApi
  class Application < Hanami::Application
    # ...

    configure :test do
      handle_exceptions true
    end

    # ....
  end
end

Or we could change it for just this one controller by adding configure { handle_exceptions true } to our controller. But then we’d also change it for the development environment. Which may not be what we want if our exception handling is more general. Usually we wish to see the stack trace in development mode.

If you want to know more about exception handling in Hanami, have a look at the documentation.

Twitter Facebook

Want to learn more about Hanami?

We have written an extensive Getting Started guide for curious people like you.