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:
Dynamic error pages is the second reference in the question. This worked just fine for me.
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.
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:
Add the following setting to
config/environments/production.rb
# Route exceptions to the application router vs. defaultconfig.exceptions_app = self.routes
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 theerrors
controller with a parametercode
that has the value of the status code.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
Create the pages themselves. I use
.erb
Here isapp/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.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!