Hoopla!

now with extra whiz-bang!

Hoopla!

Rails: Custom 404 Pages

August 05, 2006 · 10 comments

I launched JCFootballProspects.com a while ago and things have been going smoothly but the site is stuck with the nasty black-on-white error messages when somebody types in a wrong address.

I’m now at the point where I want to give out some fancy 404 pages and I’ve been looking around at what other folks have done. I’m amazed to find that all the examples I can find have a 404.html page in the `#{RAILS_ROOT}/public` directory. I’m sure it’s effective to just have a plain-html file that gets served up for errors (and it’s certainly a lot better than the default ugly stuff) but I’m convinced there are advantages to having application-provided 404 pages as well.

There are three different kinds of errors I trap and respond to: * A user gives incorrect or insufficient parameters to a page view * A user attempts to access an action that doesn’t exist * A user attempts to access a controller that doesn’t exist

The first is easily handled – even the AWDWR book shows in an early example that you can output a flash message if something goes wrong with a request. The second is only slightly more complicated but, luckily, Ruby has a great way to respond to missing actions on controllers using method_missing. The solution to the third is provided by Rails’ routing capabilities.

How to hook up an app-driven 404 page: ==== The first thing I did was added a view to my default controller (mine is called ‘home’). I created ./app/views/home/404.rhtml and put just some basic stuff in it:
<%= content_tag 'h2', 'Whoops!' %>

<%= content_tag 'h3', 'Page not found' %>

[insert message here]

The next step was to add method_missing to my application controller:
1
2
3
4
5
6
7
8
9
10
11
class ApplicationController < ActionController::Base

  ...

  def method_missing(methodname, *args)
   @methodname = methodname
   @args = args
   render 'home/404', :status => 404
  end

end
And one super low priority route (put it at the very bottom) finishes off the job:
  map.error ':controllername',  :controller => 'home',
                              :action => '404'

Tags:········

10 responses so far ↓

  • 1 sasha // Aug 12, 2006 at 04:48 PM

    there is another more generic way that will catch all the errors, you can inspect the exception to route to the right view, and possibly log the trace: in application.rb
    def rescue_action_in_public(exception)
      # do something based on exception
      message = exception.backtrace.join("\n") unless exception
      render :file => "#{RAILS_ROOT}/public/404.html", :layout => false, :status => 404
    end
      
    def local_request?
      false
    end
    
    thanks for the CSV tip. doc is atrocious
  • 2 Danger // Aug 13, 2006 at 04:33 AM

    That's brilliant. I'd been wondering where the default rescue page was coming from. I won't bother to edit this post to reflect your mad new style but I hope everybody reads your comment.
  • 3 Ben // Feb 23, 2007 at 02:09 PM

    Surely you can just change the error documents in /Public/...?
  • 4 Danger // Feb 23, 2007 at 04:09 PM

    It's true - you can just change 400.html to read what you want. But this allows you to customize the layout. You can still show the person logged in at the top-right of the screen (if that's your thing) along with any other session vars, you can keep your layout, and (should you be super-tricky) you can customize the message based on what they were looking for. But you're right - 400.html is fine for a lot of folks.
  • 5 Chris // Nov 28, 2007 at 07:38 AM

    The problem with using a route like “map.error ’:controllername’” is that it won’t catch ‘mysite.com/jibberish/morejibberish’. For a brickwall catch-all route, you’re best off using a wildcard route: “map.error ‘*url’ :controller => ‘home’ :action => ‘404’”

  • 6 Anthony // Mar 11, 2008 at 11:33 AM

    sorry, i don’t think i quite get this. So basically, i did the changes to my application controller and added the view, modified the routes, but when i type in some rndom non-existing url, i get an error telling me that

    ‘You called render with invalid options : application/404’

    (in my case, i don’t use “home”, but application)

    ny idea what exactly this means?

    A

  • 7 Jack Danger // Mar 11, 2008 at 05:54 PM

    Anthony: Try “render :template => ‘application/404’”

    This article badly needs to be updated.

  • 8 Tony Mcintyre // Jun 14, 2008 at 03:20 AM

    teraglin nizam rendlewood reorient unenraged phonautographically reservery sulphocarbamic Plymouth Creek Christian Church http://www.mnpro.com/

  • 9 sweetperceptions // Jul 05, 2008 at 08:25 PM

    What about custom 500 pages? it surely won’t be caught by the low priority route entry.. what are your thoughts?

  • 10 sweetperceptions // Jul 06, 2008 at 01:44 AM

    I just found out how to catch the 500 error messages. You can read it here: http://coderkitty.sweetperceptions.com/2008/7/6/meaningful-404s-and-500s

Leave a Comment