Rails: How do I create a custom 404 error page that uses the asset pipeline?

Avery picture Avery · Jun 16, 2014 · Viewed 32.1k times · Source

There are many solutions for creating customized error handling pages, but almost none for Rails 4:

The standard answer of encouraging people to modify 404.html in /public doesn't work for me because I want to use the CSS theme that resides in the asset pipeline. Is there a way that html files can access those styles defined in the asset pipeline? If not, is there a way to create a custom error handler that has access to the pipeline?

Answer

Douglas Lovell picture Douglas Lovell · Oct 9, 2014

For Rails 4.1 I like this answer, add an asset type better; however I have not tried it. On Rails 4.0.8, these three references helped me:

  1. Dynamic error pages is the second reference in the question. This worked just fine for me.

  2. Custom error pages may have cribbed from the first reference, or the other way around, but goes the extra mile by adding some information about testing with Capybara.

  3. I did not do the Capybara testing because I didn't want to change the test configuration; however, RSpec-Rails Request Specs clued me in to test these requests independently and see that they complete and return the correct content.

What follows is a nutshell description of what is taught by the three references:

  1. Add the following setting to config/environments/production.rb

    # Route exceptions to the application router vs. default
    config.exceptions_app = self.routes
    
  2. Edit the routing configuration, config/routes.rb to direct the error pages to an errors controller

      # error pages
      %w( 404 422 500 503 ).each do |code|
        get code, :to => "errors#show", :code => code
      end
    

    will route the 404, 422, 500, and 503 page requests to the show action of the errors controller with a parameter code that has the value of the status code.

  3. Create the controller, app/controllers/errors_controller.rb. Here is the entire content:

    class ErrorsController < ApplicationController
    
      def show
        status_code = params[:code] || 500
        flash.alert = "Status #{status_code}"
        render status_code.to_s, status: status_code
      end
    
    end
    

    My preference was to set a status message on flash.alert

  4. Create the pages themselves. I use .erb Here is app/views/errors/500.html.erb

    <p>Our apology.  Your request caused an error.</p>
    <%= render 'product_description' %>
    

    So you see that you can render a partial. The page renders with all of the layout boilerplate from app/views/layouts/application.html.erb or any other layout boilerplate that you have configured. That includes the <div id='alert'><%= alert %></div> that displays the status message from the flash.

  5. Tested with RSpec by adding a test file, spec/requests/errors_request_spec.rb. Here is abbreviated content of that file that shows a test of the 500 status page:

    require 'rails_helper'
    
    RSpec.describe "errors", :type => :request do
    
      it "displays the 500 page" do
        get "/500"
        assert_select 'div#alert', 'Status 500'
        assert_select 'div[itemtype]'
      end
    
    end
    

    The first assertion checks for the flash alert. The second assertion checks for the partial.