Rails application raise error on various context. for example, ActiveRecord find method raise ActiveRecord::RecordNotFound
when it couldn’t find record with given condition.
Rails application has built-in error handler which is in Rack
middleware. In case of ActiveRecord::RecordNotFound
, Rails API application respond with error described on below.
Since the rails always ready to handle the error, You can focus on write your business logic and feel free from rescue error. This article introduces several ways to handle error in centralized way from easy way to elegant way.
Easy Way — Just leave the error raised
You can simply let your logic raise an error if it a response is enough from Rack middleware. You can write the controller like following.
Good Way — Let ApplicationController handle specific exceptions
ActionController
provides rescue_from
method which handle errors raised from the controller. Here is the example from Rails API document.
It’s good enough, but the modular approach is better way since ApplicationController may have bunch of stuffs to do.
Better Way — Let the module handle it
Let’s create the module for ApplicationController to handle errors. First of all, define the spec of error response.
Let’s write the module. This module has Handler
and several error classes. Handler
has logics for rescuing error written in above.
Let the application autoload classes and modules in lib directory.
Finally, include ApiException::Handler
module in ApplicationController. Every error in controllers extend ApplicationController would be handled by ApiException::Handler
Best Way — Let’s make an advance in ApiException Module
Let’s take a closer look at the way above.
The Problem — It has a difficulty to handle rails built-in exceptions
Rails built-in exceptions (e.g. ActiveRecord::RecordNotFound
) would not respond with our spec. Following is a simple way to overcome.
With this approach, you always re-raise error on every built-in errors and even difficult to use messages in built-in error. Instead this, let the module handle both your custom error and built-in error.
Note that you can see the list of built-in errors handled by Rack
with command below.
$ rails r "pp ActionDispatch::ExceptionWrapper.rescue_responses"
The problem — Bad readability
You would write 8-line codes to define error class which has deadly simple information.
To improve readability, let describe error information in hash.
Thanks to ruby is dynamic language, you can define class on runtime. Let ApiException::Handler
find or create the error class by the name before executing rescue_from
method.
Improvement — Additional slot for detail message
Built-in error throw additional information sometimes. for example, ActiveRecord validator raise ActiveRecord::RecordInvalid
with detailed message.
Post.create!
>> ActiveRecord::RecordInvalid (Validation failed: Title can't be blank)
You can even raise your own error with additional message.
raise StandardError, "My awesome error message"
>> StandardError (My awesome error message)
Let the handler respond with detailed message in detail
field if raised error has an additional message.
The complete code is following. the toy project for this article in here