Rails applications and 500 errors

Matt Berther bio photo By Matt Berther Comment

The default scaffolding generated for a model has the capability of throwing a number of exceptions, resulting in a 500 level response status being sent to the client. The largest cause of these exceptions is the default behavior of ActiveRecord::Base.find. If find can't locate a record that matches the user-supplied identified, then the method throws an ActiveRecord::RecordException. Take a look at the default code generated for a controller utilizing a User model.

def show
  @user = User.find(params[:id])
end

Fairly straightforward, and without looking at any edge cases, it's very clear, clean and concise code. As you probably expected, Rails created something that lets you easily work around this. The dynamic finder methods do not throw exceptions, rather they return nil if a record is not found. If you haven't learned about the dynamic finder methods, you should really learn more about them. By rewriting the generated scaffold code slightly, we are able to present a friendly message to the end user letting them know that a record was not found.

def show
  @user = User.find_by_id(params[:id])
  render(:file => 'public/404.html', :layout => true, :status => 404) and return unless @user
end

As you can see, the render method has the capability of setting the layout, so you can have a nice consistent look and feel across your 404 page, as well as the status code which signifies to the browser that the resource was not found.

This new code is validated by these rspec tests:

describe "Given a request to show an invalid user id" do
  setup do
    User.stub!(:find_by_id)
    get :show, :id => "invalid"
  end

  it "should not assign a user"
    assigns[:user].should == nil
  end

  it "should return a status of 404" do
    response.headers["Status"].should == "404 Not Found"
  end

  it "should render a 404 file" do
    response.should render_template("public/404.html")
  end
end

This little technique will certainly help keep the size of the production log file down as well as present a nice interface to your users should a request for an invalid record id come in.