7

How does one implement dynamic, custom error pages in Rails?

For example a custom 404 error page using your application.html.erb layout and some dynamic fields in the page.

Also, how does one test this from a local machine?

5 Answers 5

13

I looked at a few blog posts on Google on how to do this, unfortunately most seem to rely on polluting your ApplicationController.

What I did instead was to create a template with the 404 message then use that template to update the public/404.html file from a rake task:

# Rake file to generate static 404 page

file "public/404.html" => ["app/views/layouts/application.html.erb"] do |t|
    print "Updating 404 page\n"
    `curl --silent http://locahost/content/error404 -o public/404.html`
end 

Now whenever I update my global layout the 404 page gets updated automatically.

Sign up to request clarification or add additional context in comments.

Comments

9

just add the following to your ApplicationController:

  rescue_from ActiveRecord::RecordNotFound, :with => :render_record_not_found

  # Catch record not found for Active Record
  def render_record_not_found
    render :template => "shared/catchmissingpage", :layout => false, :status => 404
  end

  # Catches any missing methods and calls the general render_missing_page method
  def method_missing(*args)
    render_missing_page # calls my common 404 rendering method
  end

  # General method to render a 404
  def render_missing_page
    render :template => "shared/catchmissingpage", :layout => false, :status => 404
  end

You can customize the render call (use your templates, use a layout etc.) and catch errors this way. Now it catches missing method and record_not_found for you, but maybe there are cases where you want to display a 500 Error page so you can just go ahead, use this approach and make it fit for you.

For testing from a local machine, it just works like that. if you only want it to work in production mode, add a

 if ENV['RAILS_ENV'] == 'production'

and you're fine.

1 Comment

On Rails 3.1 this isn't working for me for general 404 pages, only for records not found. Instead it keeps pulling from public/404.html and if that file is missing it just renders an empty page.
6

Check Henrik Nyh's post out. Others too can be found via google.

The idea behind: Rails seems to rendering public/404.html for 404 errors.

  • You could then overwrite the page if you want to display a static field
  • For dynamic content, it seems you can override a framework method to hook in and redirect to render your dynamic page.

ActionController::Rescue defines a rescue_action_in_public that calls render_optional_error_file.

2 Comments

@AnApp could be.. it's an old answer - you could post the Rails 3 version as a separate answer.
The link goes to a Nginx 404 page. :-) The new link is mattbrictson.com/dynamic-rails-error-pages
3

If you do decide to create a dynamic 404 (or other status code) page be sure to remove the corresponding html file from /public (if it exists)

Comments

1

On the testing front, a really good way to do this (for development purposes, at least) is to use Passenger and set the rails environment to production (or comment out "RailsEnv development" in the site config). At least this way you can mimic how it works in production.

But, to get this done, I have a variety of settings files that get parsed on startup and are picked up based on the environment. One of the settings is whether to show error pages (AppSettings.show_page_errors?). Then in my Application Controller, I have

  if !AppSettings.show_page_errors?
    alias_method :rescue_action_locally, :rescue_action_in_public
  end

So, it is generally set to the default settings, but sometimes I really need to see what exactly is going on, so I can turn it off on production.

The other step is to use custom pages. In my case, I have templates based on the error that also include a form to submit to google forms (since my server may be broken). To do that, put this (and change as needed) in your Application Controller:

def render_optional_error_file(status_code)
  status = interpret_status(status_code)
  render :template => "/errors/#{status.to_s[0,3]}.html.haml", :status => status, :layout => 'application.html.haml' if [404, 422, 500].include?(status)
  render :template => "/errors/unknown.html.haml", :status => status, :layout => 'application.html.haml' unless [404, 422, 500].include?(status)
end

This will render the status codes 404, 422, and 500 using the template, but otherwise it uses unknown. If you need to handle others, it's just a matter of updating this method and adding the template.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.