NEW 2017
We created a gem for this - exception_handler

How it works
All Rails exceptions are handled using the config.exceptions_app callback. This is assigned in the config/application.rb or config/environments/*.rb files - this should be a callback:

Whenever Rails accesses an error, it calls ShowExceptions middleware. This calls exception_app and sends the entire request (including exception ) to exceptions_app :

exceptions_app should provide an answer . If not, failsafe loads:
# show_exceptions.rb#L38 def render_exception(env, exception) wrapper = ExceptionWrapper.new(env, exception) status = wrapper.status_code env["action_dispatch.exception"] = wrapper.exception env["PATH_INFO"] = "/#{status}" response = @exceptions_app.call(request.env) # => exceptions_app callback response[1]["X-Cascade"] == "pass" ? pass_response(status) : response rescue Exception => failsafe_error # => raised if exceptions_app false $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" FAILSAFE_RESPONSE end
failsafe is stored as FAILSAFE_RESPONSE at the top of ShowExceptions .
Custom error pages
If you want to create your own error pages, you need to enter your own callback in config.exceptions_app . This can be done either in the application or using a gem:

Notice how the call method is used - this is how the callback works. Rails ( env ) is called when a request is received from the Internet; when an exception occurs, env is passed exceptions_app .
The quality of exception handling will depend on how you manage env . It is important; The self.routes link self.routes not take the environment forward.
It is best to handle exceptions with a separate controller. This allows you to process the request as if it were just another view providing access to layout and other components ( model / email ).
-
There are two ways to handle exceptions:
- Redefining
404 Routes - Controller call
Our gem was designed around our controller - it is called every time an exception raised. This gives full control over the exclusion process, allowing 100% corporate identity :

ExceptionHandler now the leading personal graphical error editor for Rails.
Supported for over 3 years, this is the easiest and most powerful stone for Rails. It runs 100% on Rails 5 and has already been downloaded over 70,000 times.
Gem
The latest version 0.7.0 has the following updates:
- Custom exceptions
- Match exception (select which exceptions to handle)
- Email Notifications
- Backend models
- Chain Integration 4+
You can read it here .
Rails Exception Exception
If you are not interested in the gem, let me explain the process:
All Rails exceptions are handled using the config.exceptions_app callback. This is assigned in the config/application.rb or config/environments/*.rb files - this should be a callback:

Whenever your application ShowExceptions an exception, ShowExceptions middleware is called. This middleware config.exceptions_app an exception in request and redirects it to the config.exceptions_app .
By default, config.exceptions_app points to routes. This is why Rails comes with 404.html , 500.html and 422.html in the public folder.
If you want to create custom exception pages, you need to override the config.exceptions_app - pass the erroneous request to the appropriate handler, be it controller or route :
[[middleware]]
Two ways to effectively manage: either send erroneous requests for routes, or call the controller.
The easiest and most common way is to redirect the request to routes; Unfortunately, this ignores the request and does not allow you to describe exceptions in detail.
The best way is to invoke a separate controller. This will allow you to transfer the entire request, allowing you to save it, send it by e-mail, or do a few more things.
-
400/500 Errors
Rails can only respond with valid HTTP errors .
While the application exception may be different, the return status code must be either 40x or 50x . It complies with the HTTP specification and describes here β΄

This means that no matter what exception handling solution you use / create, Rails should return 40x or 50x errors to the browser.
In other words, custom error pages have little to do with the type of exception β more so than how you catch and maintain the browser response.
By default, Rails does this with 404.html , 422.html and 500.html files in the public folder. If you want to handle the exception stream yourself, you need to delete these files and pass the erroneous requests to your own exceptions_app callback.
This can be done using routes or using the controller (which I will explain now):
1. Routes
The easiest way is to let routes handle it.
This method is bloated and requires several actions. It is also difficult to manage answers.
This tutorial explains:

This shows how to directly replace exceptions_app with routes:
Here is the code I have (Ruby 2.0.0, Rails 4.0):
Application configuration
Routes
#config/routes.rb if Rails.env.production? get '404', to: 'application#page_not_found' get '422', to: 'application#server_error' get '500', to: 'application#server_error' end
Application controller
#controllers/application_controller.rb def page_not_found respond_to do |format| format.html { render template: 'errors/not_found_error', layout: 'layouts/application', status: 404 } format.all { render nothing: true, status: 404 } end end def server_error respond_to do |format| format.html { render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500 } format.all { render nothing: true, status: 500} end end
Error layout (fully static - only for server errors)
#views/layouts/error.html.erb <!DOCTYPE html> <html> <head> <title><%= action_name.titleize %> :: <%= site_name %></title> <%= csrf_meta_tags %> <style> body { background: #fff; font-family: Helvetica, Arial, Sans-Serif; font-size: 14px; } .error_container { display: block; margin: auto; margin: 10% auto 0 auto; width: 40%; } .error_container .error { display: block; text-align: center; } .error_container .error img { display: block; margin: 0 auto 25px auto; } .error_container .message strong { font-weight: bold; color: #f00; } </style> </head> <body> <div class="error_container"> <%= yield %> </div> </body> </html>
Error Views
#views/errors/not_found_error.html.erb <div class="error"> <h2>Sorry, this page has moved, or doesn't exist!</h2> </div> #views/errors/internal_server_error.html.erb <div class="error"> <div class="message"> <strong>Error!</strong> We're sorry, but our server is experiencing problems :( </div> </div>
While many prefer the βroutesβ method for their simplicity, it is neither efficient nor modular. In fact, if your application has some sort of object orientation, you will quickly drop it as a hack.
A much louder way is to use a custom controller to detect a pure exception. This way you can build the thread according to the general structure of your application:
2. Controller
Another option is to redirect all requests to the controller.
This is infinitely more powerful, as it allows you to accept a request (exception) and pass it into views, managing it in the backend. This will save it to the database.
This gist shows how:

This means that we can connect to the middleware and pass the entire request to the controller.
If this controller is supported by the model and views, we can extract it into a gem (this is what we did). If you want to do it manually, here's how:
-
Config
The beauty of this method is that it is config.exceptions_app directly to config.exceptions_app . This means that any exception can be handled initially, providing greater efficiency. To make sure this works, you need to put the following code in config/application.rb ( exceptions_app only works in production - development shows errors):
To check, you can set the "local" queries to false:
#config/environments/development.rb config.consider_all_requests_local = false
-
controller
The next step is to add an exception controller. Although this can be handled in application_controller , it is much better to extract it yourself. Pay attention to the call from application.rb - ExceptionController.action(:show) :
-
Views
There are two views you need to add in order to make this work.
The first is the exception/show view, and the second is layouts/error . First, give an exception_contoller#show representation, and secondly for 500 internal server errors.
#app/views/exception/show.html.erb <h1><%= details[:name] %></h1> <p><%= details[:message] %></p> #app/views/layouts/error.html.erb (for 500 internal server errors) <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Error</title> <style> html { height: 100%; background: #fff; } body { font-family: Helvetica, Arial, Sans-Serif; font-size: 14px; } .error_container { display: block; margin: auto; margin: 10% auto 0 auto; width: 40%; } .error_container .error { display: block; text-align: center; } .error_container .error img { display: block; margin: 0 auto 15px auto; } .error_container .message > * { display: block; } .error_container .message strong { font-weight: bold; color: #f00; } </style> </head> <body> <div class="error_container"><%= yield %></div> </body> </html>
Conclusion
An exception does not matter as an error code.
When Rails throws an exception, it assigns one of the above HTTP response codes. This allows your browser to determine if the request was successful.
When using exceptions, you need to make sure that you can handle 40* errors (which will usually use the same layout as the rest of your application) and 50* errors (which will require their own layout).
In both cases, it is best to use a separate exception controller, which allows you to control exception as an object.