Seventh Octave

We're Starters. We write about culture, design and the business of software.

We believe software should be beautiful and inspired. Follow us as we build @TechOctave.

Rails 3.0 rescue from Routing Error Solution TVD Nov 11

10 comments Latest by TVD

Well, I've got good news and I've got bad news. As of Rails 3.0.1, using rescue_from in your ApplicationController to recover from a routing error is broken! That's the bad news.

The good news is I have a solution that will keep you in unison with the Rails Core Team. The Rails team has promised a fix some time in Rails 3.1. Until then, I've got readers and I've got customers and I shudder at the thought of showing them a generic error page. Rails 3.0 rescue_from routing error solution

The Situation

It's bad enough an error has occurred in the first place. At that point I want to take control of the situation and rescue my audience from a bad experience back to enjoyment!

Previously in Rails 2.3.8 and below you could handle routing errors elegantly using rescue_from ActionController::RoutingError:

class ApplicationController < ActionController::Base
  rescue_from ActionController::RoutingError, :with => :render_404

  private
  def render_404(exception = nil)
    if exception
        logger.info "Rendering 404: #{exception.message}"
    end

    render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
  end
end

However, things are a little different in Rails 3. The reality is, as of Rails 3.0.1, ApplicationController can't catch ActionController::RoutingError and thus, we cannot take advantage of rescue_from like we used to.

Now, for those of you who don't know, I'm a realist. So, I'm not expecting the Rails team to spring a solution overnight.

Personally, I'm going to wait for the Official fix from the Rails Core Team. In the meantime, I need a simple, no side effects solution that I can use right now!

Simple Solution

This is one of those times when it's great to be a developer. There is nothing we can't solve with a little elbow grease and ingenuity.

Expanding on the suggestion given by the Rails core team, here's the solution I use to handle routing errors in Rails 3.0:

config/routes.rb

This code should go to the end of your routes.rb file. That way it will be given the least priority and therefore, act as a wildcard catchall for all those rogue url resources.

Yourapp::Application.routes.draw do
  #Last route in routes.rb
  match '*a', :to => 'errors#routing'
end

NOTE: The "a" is actually a parameter in the Rails 3 Route Globbing technique. For example, if your url was /this-url-does-not-exist, then params[:a] equals "/this-url-does-not-exist". So be as creative as you'd like handling that rogue route.

app/controllers/errors_controller.rb

Here, I handle my routing errors. I leverage previous 404 handling code from my original ApplicationController mentioned above. So, my errors_controller.rb looks like this:

class ErrorsController < ApplicationController
  def routing
    render_404
  end
end

However, feel free to modify to fit your individual needs. Everyone's situation will be slightly different. For example, if you're not going to reuse your 404 error handling logic, then here's the full ErrorsController without inheritance:

class ErrorsController < ApplicationController
  def routing
   render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
  end
end

I'm big on keeping things simple. I believe solutions should be simple without being simplistic. Like I said earlier, I look forward to an Official solution from the Rails Core Team. Until then, this gets the job done!

Well, I encourage you to dig into Rails 3 and have a little fun. And if you have another solution, post here so we can discuss. Until next time Beloved, take care!

If you enjoyed this post, subscribe for updates (it's free).

Email Address:

10 comments so far

Miguel Benitez 12 Nov 10

Great solution! Thanks!

Lonny Eachus 12 Nov 10

In my opinion that qualifies as an "elegant" solution to what otherwise could be an intractable problem. Cheers.

Billee D. 13 Nov 10

That is a beautiful thing, my friend.

TVD 13 Nov 10

@Lonny intractable truly sums up this dilemma. There is much value to be gained in customizing error messages. I hope this gets you on your way.

Glenn Roberts 08 Dec 10

Thanks Tian. We can definitely use this on our current project.

TVD 08 Dec 10

@Glenn That's great to hear! Rails is a wonderful platform to build from. I wholeheartedly wish you luck and success on your forthcoming project. I look forward to seeing it take off!

Chip Miller 17 Dec 10

Very nice! You can also add unless ActionController::Base.consider_all_requests_local to view the errors while developing. (like so...) match '*a', :to => 'errors#rescue_from_routing_error' unless ActionController::Base.consider_all_requests_local

m 02 Feb 11

Works for me. Thank you!

Thomas Glasgow 01 Mar 11

I used this old trick since attackers were issuing requests that looked like /config.php?user_id=blabla&=etc The problem I am now facing is when spammers try to bypass the authenticity token protection in my contact forms. I no longer can intercept such errors with rescue_from and my exception notifier kicks in for "false" alerts. Any idea how to fix that?

TVD 01 Mar 11

@Thomas That's got to be incredibly frustrating. If it were me, I'd use Akismet or TypePad AntiSpam until Rails 3.1 is released - the rescue_from issue should be fixed in that release.

Comments are closed