Jan 142007
 

I spent a couple of hours trying to figure out how to handle 404 and 500 errors in Rails. This is not simple and actually really annoying. Hopefully future versions clean this up because right now it sucks pretty badly. Anyways, I found a page on the wiki and some other blogs, but the issue was that they wouldn’t handle all the cases. So, here’s the solution:

1. Edit your app/controllers/application.rb file and add these three methods:

The first method will be explained in the next step. The second method is the method that Rails calls to handle most errors. This method will not capture a certain class of errors where neither the controller nor the action requested exist. The third method tells rails to stop sucking. Normally Rails handles requests made to localhost or 127.0.0.1 differently than all others. This might be for debugging purposes, but it sucks when testing error handling.

2. Edit config/routes.rb and add this line TO THE END OF THE FILE:

This tells Rails that if it can’t find any other route to handle the request (i.e. the *path) it should call the rescue_404 action on the application controller (the first method above).

3. Edit config/environments/development.rb and add this line:

This additionally tells Rails to stop sucking and stop handling requests to localhost and 127.0.0.1 differently.

Anyways, happy coding.

  8 Responses to “Handling Rails 404 and 500 errors”

  1. yo! have you had much experience with product deployments yet and stuff like lighthttpd or mongrel? just curious. i’ve deployed with apache/fastcgi…wanted to see your thoughts on tuning production servers.

  2. I’ve looked at lighthttpd and mongrel, but I haven’t messed with it. FastCGI is okay for my needs and it is pretty simple to configure, so I haven’t done that fully yet. There are a few things about it that suck, but for the most part it works fine. I don’t like how it has to reload the entire rails app if it hasn’t been hit in a while. That causes pauses sometimes.

    However, from what I’ve seen with SSL, lighthttpd and mongrel can be a bit of a configuration headache, but I could be wrong.

    In terms of tuning, it is mostly in the application code and then tuning the apache/fcgi connectors (timeouts mostly). Usually I haven’t had to tune much and things work fine.

    What kind of tuning have you been doing?

  3. Seems you categorized this post incorrectly (“Java”, should be “Ruby/Rails”).

  4. In Rails 1.1, the public/404.html page is rendered on a 404 error. In Rails 1.2, public/500.html is rendered on 500 errors. If you need to render dynamic content, you will need to override the rescue_action_in_public method to point it towards your templates.

    The local request check is there so that developers can receive debug information in a production environment by logging onto the prod box and hitting it directly. This distinction is only used when your app is started up in production mode. In development, all requests are considered local.

    I’ve found that the easiest way to test error pages is to fire up your application in production mode and it it from another computer. If you don’t have another computer handy, overriding the local_request? method to always return false will allow you to see what external users do.

  5. The problem still remains that if you don’t have a controller or view for the URL, then Rails freaks out. So, you need to conenct the catch everything URL just in case. I have a lot of requests, mostly folks doing bad links in Word Documents and Excel spreadsheets, that come in for crap like:

    /Document+C:\docs\blah/Open.aspx?foo=bar

    So, I wanted to handle these since I don’t have a controller or view directory named Document. In order to pull this off you need a route that handles everything that doesn’t exist. This was the tough part to figure out.

    Plus, the fact that Rails tries to be smart about localhost and remote hosts seems to be really lame. I want development to behave just like production, or at least as close as possible, so I can debug things properly. Not a big fan of getting things into production and crap happens that I didn’t expect. Just personal preference I guess.

  6. There is a better way to “tell rails to stop sucking” without changing local_request? or the consider_all_request_local.

    In addition to the rescue_action_in_public method there are 2 other rescue methods defined … rescue_action & rescue_action_locally. The easier approach is to define:

    def rescue_action_locally(exception)
    rescue_action_in_public(exception)
    end

    so that your local and public exceptions fail the same way plus you only have to change that one call if you decide to go back to the other way where you get more diagnostic info on a local exception.

    Thanks for your post it got me looking in the right direction and now I am trapping my errors

  7. You could also do the following at the end of your routes.rb file.

    map.connect “*anything”, :controller => “your_controller”, :action => “your_action”

    This will catch all badly formed requests and send them to the designated controller and view. If you want to see what was passed, you can access them via params[:anything]. You may also need a separate layout for your controller depending on what you’re doing. This will behave the same regardless of environment.

    I found this out coincidently by reading the routes section in AWD w/Rails 2ed. pg. 399 and then looking at the rails rdoc for ActionController::Routing, Route globbing.

  8. In rails 3.1.3 it shows an /* undefined method `consider_all_requests_local=’ for ActionController::Base:Class (NoMethodError) */
    May i know what is the solution for that NoMethodError problem…

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">