Meaningful 404s and 500s

My site is still young and contents aren't that many right now, but what I really enjoy doing is making all the pages look better and be more usable. One thing that I recently gave thought on were my error pages.

I tried to advertently access a missing page, and I was greeted with my old one liner text and a suggestion to visit my blog. At the bottom of it was my twitter feed. It had the same layout as the rest of my site's pages and had the usual links on the right side. Funny though, when you access this missing page, it did take the usual loading time only to be displayed with such a page. And so I thought it needed a revamp.

Aside from thoughts on usability, I wanted really to put some things that can't really be done from plain HTML. I wanted to put some of my recent posts as suggestions for visits. These and I get stuck with an HTML file for 404 or 500. And so, after some time of research, I found a very nice solution.

I came across a wiki entry on how to configure the error page of your application and proposed that ActionController::Rescue's rescue_action_in_public will be overriden in the application.rb of your application.

Rails' 404 and 500 error pages are located in the public directory of your application and they are in plain HTML files. The only way for us to use layouts is to put it somewhere inside the application where layouts and the ActionController can do its tasks.

A 404 error can be triggered when there is a path retrieved and its not found anywhere in the application. The easiest way to catch this and point it to a controller/method. In your config/routes.rb, add the lowest priority route definition:

map.connect '*path', :controller => 'application', :action => 'rescue_404' unless ::ActionController::Base.consider_all_requests_local

When all routes have been checked and that the currently requested file is not found, it lands on the lowest priority route and makes a request to application.rb's rescue_404. The rescue_action_in_public receives the call and directs to the right template. In your application.rb, put these codes:

def rescue_404          rescue_action_in_public(ActionController::RoutingError)
end 

def rescue_action_in_public(exception)
#maybe gather up some data you''d want to put in your error page
case exception
when ActionController::InvalidAuthenticityToken
when ArgumentError
when SyntaxError
render :template => "shared/error500", :layout => "error", :status => "500"
else
render :template => "shared/error404", :layout => "error", :status => "404"
end
end

def local_request?
return false
end

This works, but these codes just catches the 404 errors, but not the 500s. I found this out the hard way, but I'm saving you time and letting you know how to work this out. By reading further, some are suggesting to put specific rescue_action_in_public methods per controller. I think that this would be too tiring, and so having a DRY implementation of it would be better.

Since some errors occur before any controllers are loaded, we need to create a "pre-controller" approach. Create a pre controller in your lib folder. You can name it as you please. Put in the following lines:

class ActionController::Base
def rescue_action_in_public(exception) #maybe gather up some data you'd want to put in your error page
case exception
when ActiveRecord::RecordNotFound
when ActiveRecord::RecordInvalid
when ActionController::RoutingError
when ActionController::UnknownController
when ActionController::UnknownAction
when ActionController::MethodNotAllowed
render :template => "shared/error404", :layout => "error", :status => "404"
else
render :template => "shared/error500", :layout => "error", :status => "500"
end 
end
end

Strap the loading of this file into your config/environment.rb like this:

require 'error_catcher'

or whatever you named your file with. After that, you now need to create your templates and layout files as necessary. Well, I'm sure you already know how to work that out. :)

That's it. I haven't found any bright alternatives, but it works for me, and I think it just needs a little cleaning and it'll be better.

Maricris Nonato

I turn tech ideas into websites using Ruby. I'm a budding Buddhist practitioner, passionate about health & fitness, and a cat fanatic!