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

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


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. defaultconfig.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  endend

    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]'  endend

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


We've made a gem which does this for you: exception_handler.

There is also a great tutorial here.

I also wrote an extensive answer on the subject here.

Middleware

# config/application.rbconfig.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }

Controller

# app/controllers/exception_controller.rbclass ExceptionController < ApplicationController  respond_to :json, :js, :html  before_action :set_status  def show    respond_with @status  end  private  def set_status    def status      @exception = env['action_dispatch.exception']      @status    = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code      @response  = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name]    end  endend

View

# app/views/exception/show.html.erb<h1>404 error</h1>

This is very simple version - I can explain more if you wish.

Basically, you need to hook into the config.exceptions_app middleware, it will capture any exception in the middleware stack (as opposed to rendering the entire environment), allowing you to send the request to your own controller#action.

If you comment, I'll help you out some more if you want!