No Ruby on Rails developer want to see them in production: Exceptions! By default every Rails project comes with two static files that get rendered when an exception in production occurs.
The problem with these pages is, that they do not fit well into any design and do not tell the user what really went wrong. In Rails 2.x it was possible to catch all thrown exceptions using the
rescue_from method in the ApplicationController.
In Rails 3 however routing was extracted into its own middleware called ActionDispatch. While this is a good thing, it has created a lot of confusion on how to handle
ActionController::RoutingError. Having routing done by a middleware means that in case of a routing error the ApplicationController will not get executed, and therefore
rescue_from can not handle any routing errors.
ActionDispatch provides a class
ActionDispatch::ShowException to handle exceptions that happen in the middleware.
Over time the community has proposed several solutions to solve this problem.
Matthew Gibbons proposed in his article Rails 3.0 Exception Handling to override the ShowException class in an initializer. The overriden method
render_exception will call a method on a custom ErrorsController and render out the exception in the layout of the page.
The advantage of this approach is that the user will see the error message rendered in the layout of the page and it is up to you on how to properly present the error message to the user. While this was a viable solution for Rails 3.0 and 3.1, in Rails 3.2 it will not work anymore.
Catch all route
Tian from TechOctave proposed in his article Rails 3.0 rescue from Routing Error Solution to introduce a catch all route in
He mentions that the
a "is actually a parameter in the Rails 3 Route Globbing technique. For example, if your url was /this-url-does-not-exist, then
/this-url-does-not-exist". While this solution looks nice at first, it will create problems when your app uses engines.
The release notes of Rails 3.2 introduce a new solution to the workarounds above.
config.exceptions_appto set the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to ActionDispatch::PublicExceptions.new(Rails.public_path).
The best solution I have come across so far which utilizes the new config setting was proposed by Sjoerd Andringa. He uses a lambda expression for the setting, which gets called when an exception happens in the middleware.
The use of a lambda expression is necessary, because the controller name constant is not yet available in the initialization stage. What lambda expression does, is when
ActionDispatch catches an exception, it will invoke the lambda expression from the config with the environment variable as parameter and then call the show action on the
The controller uses
env["action_dispatch.exception"] in order to retrieve the original exception object that was raised to get the status code and message and then renders out the show view, which you can design as want. Note the first line that sets the layout in the controller to the application layout, so all views will be rendered in the same way as the rest of your app.
The original source, including the i18n file, can be found as a Gist on Github.
We have seen a few solutions to the new way of handling exceptions in Rails 3. For Rails 3.2 and up it is definitely a good solution to configure an exceptions_app and handle exceptions using a custom ExceptionsController.
What strategy do you use in your production app?