Rails 3.1 - Adding custom 404 and 500 error pages
January 5, 2012
This post was originally published in the Rambling Labs Blog on January 5, 2012.
As I said when announcing the Rambling Labs new site, we’ve been learning a lot of stuff while building it.
Something that we didn’t have the chance to implement on our current projects (but that we will be including soon), is adding custom error pages to the site. So far, what we were looking for was two things: a custom 404 error page and a custom generic 500 error page.
For the experience I have now with Rails, I thought this would be a piece of cake. Well, in fact… it would’ve been if we were using Rails 2. But guess what? The error handling behavior in Rails 3 is not what you would expect. Even worse, it’s broken for routing errors!
For what I could find on StackOverflow (question 1, question 2), in order to handle errors in a rails 3 application, this is what you have to add to the ApplicationController
:
UPDATE
The render_error
method was missing the layouts, so a funky error happened when using nested templates. The code for ApplicationController
has been updated.
UPDATE September 23rd, 2012
Some refactoring made to the ApplicationController
, DRYing up. Thanks to Alexey for his suggestion on the comments!
class ApplicationController < ActionController::Base
# ...
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: lambda { |exception| render_error 500, exception }
rescue_from ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, with: lambda { |exception| render_error 404, exception }
end
private
def render_error(status, exception)
respond_to do |format|
format.html { render template: "errors/error_#{status}", layout: 'layouts/application', status: status }
format.all { render nothing: true, status: status }
end
end
# ...
end
But, as I said before, this doesn’t work for routing errors. To solve this, first generate the ErrorsController
and the error_404
and error_500
views with this:
rails generate controller errors error_404 error_500
Add this to your just generated ErrorsController
:
class ErrorsController < ApplicationController
def error_404
@not_found_path = params[:not_found]
end
def error_500
end
end
Then, customize the error_404.html.erb
and error_500.html.erb
views (error_404.html.haml
and error_500.html.haml
in my case) in the app/views/errors
directory. This is what I got for the error_404
:
%h2 404
%div
%h3 We're sorry
%p
The content that you requested could not be found.
%p
You tried to access '#{@not_found_path}', which is not a valid page.
%p
Want to
%a{href: root_path} go back to our home page
and try again?
Also, add these lines at the end of your config/routes.rb
file:
unless Rails.application.config.consider_all_requests_local
match '*not_found', to: 'errors#error_404'
end
And remove these two routes from the config/routes.rb
file, since they are unnecessary:
get "errors/error_404"
get "errors/error_500"
One last thing. The errors are not shown on development, so to test them out and make sure that they work as expected, temporarily remove the Rails.application.config.consider_all_requests_local
condition from both the config/routes.rb
and the app/controllers/application_controller.rb
files.
That should do it for now!
Don’t forget to write the tests for your ErrorsController
and add the consider_all_requests_local
condition again.
Enjoy! :)