we develop communication with weLaika Advertising

Rails Custom Error Pages

Tags: ruby on rails, ruby
Fabrizio Monti -
Fabriziomonti
Dinosaur

Do you know how (static) error pages like 404 or 500 get rendered in a (production) Rails application?

It’s all managed by this Rack Middleware.

When called, this middleware renders an error page. By default if an HTML response is expected it will render static error pages from the /public directory. For example when this middleware receives a 500 response it will render the template found in /public/500.html.

An instance of this middleware is associated to the following Rails configuration:

config.exceptions_app sets the exceptions application invoked by the ShowException middleware when an exception happens.

Source: http://guides.rubyonrails.org/configuring.html

I want to render a dynamic error page with my application layout.

Let’s start by overwriting exceptions_app configuration:

1
2
3
4
5
6
7
8
9
# config/application.rb
# ...
module MyApp
  class Application < Rails::Application
    # ...
    config.exceptions_app = routes
    # ...
  end
end

Doing so, you can now define custom routes:

1
2
3
4
5
6
7
#config/routes.rb
Rails.application.routes.draw do
  %w(404 422 500).each do |status_code|
    get status_code, to: "errors#show", status_code: status_code
  end
  # ...
end

You have to create ErrorsController:

1
2
3
4
5
6
7
8
9
10
11
12
# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
  def show
    render status_code.to_s, status: status_code
  end

  private

  def status_code
    params[:status_code]
  end
end

and a view file for each error code defined in routes.rb. This is a skeleton for 404.html.erb:

1
2
<h1>Error#not_found</h1>
<p>Find me in app/views/error/not_found.html.erb</p>

Now you can delete the static view files public/404.html, public/422.html and public/500.html.

You are almost ready, but if you want to test this solution in development, you have to temporarily set config.consider_all_requests_local = false in config/environments/development.rb. Otherwise you are going to see the default Rails development error page instead of your custom view.

Gotchas

Will my error notification library still work?

We, at weLaika, are using Airbrake with Errbit and yes, it works. No extra work!

Why do I get a 500 error page for ActiveRecord::RecordNotFound instead of a 404 error page?

The 404 custom page you created before will be rendered only if Rails can’t find a match in config/routes.rb. Further more, since ActiveRecord::RecordNotFound is an exception, Rails does the right thing when it renders the 500 error page.

To fix this, you can use rescue_from as follows:

1
2
3
4
5
6
7
8
9
10
11
12
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # ...
  rescue_from ActiveRecord::RecordNotFound, with: :handle_record_not_found

  protected

  def handle_record_not_found
    render "errors/404", status: :not_found
  end
  # ...
end

Be careful if you have multiple rescue_from conditions, because their order matters!