Meaningful 404s and 500s

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 rescueactionin_public will be overridden 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 rescue404. The rescueaction_in_public receives the call and directs to the right template. In your application.rb, put these codes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      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 rescueactionin_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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    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. smile

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.

Recommend me on Working With Rails

If you found this post of any use, then please take the time to recommend me on Working with Rails.

6 Responses to “Meaningful 404s and 500s”

  • zeddy
     

    Thanks works a treat … this let me put the finishing touches to my site!

  • Alex
     

    I can’t seem to get this to work. I put actioncontroller in /lib/ and required it in my environment.rb and all I ever get is “uninitialized constant ActionController”

  • Alex
     

    I can’t seem to get this to work. I put actioncontroller in /lib/ and required it in my environment.rb and all I ever get is “uninitialized constant ActionController”

  • PhilT
     

    Thanks for this. Might be nice to modify this to add support for 422.

    If I do it myself I’ll post the changes :)

  • PhilT
     

    Alex, put the require at the bottom of the environment

  • Mark
     

    With Rails 2 came "rescue_from", which makes catching exceptions much easier. Check out the API for a good example.

    AFAIK this means there is no longer a need for the pre-controller lib file. However, I still seem to need the catch all route.

Leave a Reply

Hi, I've been busy you know. wink If you don't know yet, this site and my blog codes are hosted in . I recently moved to Git and found that they have support for private Git repositories. Below are details of my last git push.

Maricris S. Nonato on 26 Mar
Commit: e734bd6878629223c5067326471d7ec0aac7d6e4

Updated display of ads and popular content