Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Can no longer rescue_from ActionController::RoutingError

#4444

In Rails 2.3.x, one is able to stick this in ApplicationController to present the user with a custom 404 screen:

rescue_from(ActionController::RoutingError) { render :text => 'This is a custom 404.' }

Now in Rails 3, because routing is done as middleware (ActionDispatch), it seems that the ActionController::RoutingError that gets thrown by ActionDispatch no longer can be caught from ApplicationController -- the error is already thrown and ActionDispatch renders /templates/rescues/routing_error.erb before the controller can rescue_from the error.

Reported by Luigi Montanez · April 20th, 2010 @ 04:50 PM

State: new
Milestone: 3.1
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Ben Marini
    Ben Marini

    I verified this with a freshly generated rails app from the master branch. I haven't investigated too far, but at this point the best way I can see to do this in rails 3 is to subclass and swap out the ActionDispatch::ShowExceptions middleware. Or add a route that matches anything...is there a better way?

    April 27th, 2010 @ 03:38 AM

  2. Jean
    Jean
    • Tag changed from rails3 routes, 3.0.0.beta3 to rails3 routes, 3.0.0.beta3, rails3, rescue, routes, routingerror

    I am also hitting this problem, but my understanding of the internals of rails3 is very limited atm ...

    Couldn't we define the rescue handling as an option to the route ? (thus in route.rb)

    May 27th, 2010 @ 09:09 PM

  3. Neeraj Singh
    Neeraj Singh

    Put following code in an initializer in a rails3 app.

    module ActionDispatch
      class ShowExceptions
        def render_exception(env, exception)
          if exception.kind_of? ActionController::RoutingError
            render(500, 'it was routing error')
          else
            render(500, "some other error")
          end
        end
      end
    end
    

    There might be a better way but it works in my quick test.

    May 28th, 2010 @ 04:35 AM

  4. José Valim
    José Valim
    • Milestone set to 3.1
    • Assigned user set to José Valim
    • Importance changed from to Low

    The best way to handle a missing route in Rails 3 is by adding the following line at the end of your router:

    match "*", :to => "home#routing_error"

    And add the routing_error action. We will be working on a better solution, but it is targeted for 3.1.

    July 13th, 2010 @ 08:58 PM

  5. Hubert Łępicki
    Hubert Łępicki

    José, are you sure that your solution actually works? I think it's not working as you can't simply say match "*" to match everything in Rails 3 so far?

    July 25th, 2010 @ 05:47 PM

  6. Hubert Łępicki
    Hubert Łępicki

    I mean, you have to do it like this:

    match '/:anything', :to => "home#routing_error", :constraints => { :anything => /.*/ }

    July 25th, 2010 @ 05:52 PM

  7. Hubert Łępicki
    Hubert Łępicki

    I was giving it a bit more thought since yesterday. The solution with match "/:anything" is also not perfect for some people, as it'll override all routes from plugins/engines. I now think that best solution is to use custom rack middleware for handling 404 pages in Rails.

    July 26th, 2010 @ 08:14 AM

  8. Matthew Gibbons
    Matthew Gibbons

    The approach that I have adopted to solve this (for now) is to make a call back into the application (from ActionDispatch::ShowExcpetions#render_exception), specifically to a controller that is there for the purpose of rendering an error page. This has all the benefits of using the layout and styling of the host application. However, if a second exception occurs whilst handling the exception in this way, the original method is called, and the default is rendered.

    It is better explained here: http://accuser.cc/posts/1-rails-3-0-exception-handling

    For those who would rather copy and paste than read the above... ;)

    1. http://gist.github.com/522944
    2. http://gist.github.com/522924

    August 13th, 2010 @ 03:14 PM

  9. Andre Pankratz
  10. gucki
    gucki

    The easiest way for me was to add this very last route:

    match ''=> lambda { |env| raise ActionController::RoutingError, env["PATH_INFO"] }

    September 22nd, 2010 @ 02:34 PM

  11. gucki
    gucki

    Please ignore my previous comment, it does not work that way. Seems rails had some routes cached, so it seemed to work initially.

    September 22nd, 2010 @ 02:51 PM

  12. Ryan Bigg
    Ryan Bigg
    • Tag cleared.

    Automatic cleanup of spam.

    October 9th, 2010 @ 10:00 PM

  13. Ryan Bigg
    Ryan Bigg

    Automatic cleanup of spam.

    October 19th, 2010 @ 08:22 AM

  14. Tian Davis
    Tian Davis

    Personally, I'm waiting for an Official solution from the Rails Core Team. Until then I need a simple, no side effect solution for catching rogue routes. For now, my solution is an elaboration on José Valim's recommendation:
    http://techoctave.com/c7/posts/36-rails-3-0-rescue-from-routing-err...

    With this approach, you'll be in unison with the Rails Core Team. Moreover, I incorporate Rails 3 Route Globbing, so you get the added benefit of knowing exactly which rogue route was entered. As a result, you're free to handle routing errors as creatively as you'd like.

    November 12th, 2010 @ 12:25 AM

  15. avocade (at gmail)
    avocade (at gmail)

    Issue for more than just rescuing from routing errors, CanCan::AccessDenied too:

    http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devi...

    December 19th, 2010 @ 02:50 AM

  16. Kevin Watt
    Kevin Watt

    This is a huge bug and I feel it's priority should be raised.

    It's not acceptable for a broken link in a production app to result in a blank screen, this is why we make 404 handlers. But once the request is dispatched to rails it seems to be rails job to render the 404 page, which it can't do because of this bug.

    March 18th, 2011 @ 03:40 PM

  17. Kevin Watt
    Kevin Watt

    Just to add,
    match '/:anything', :to => "home#routing_error", :constraints => { :anything => /./ } or
    match "
    ", :to => "home#routing_error"

    Don't seem to work when the controller exists but the action doesn't. Exception log still just shows "AbstractController::ActionNotFound (The action 'rejsasdf' could not be found for HomeController):" and a blank page is rendered.

    March 18th, 2011 @ 04:56 PM

  18. Andrew White
    Andrew White

    Are you using :action as the dynamic segment name? If so Rails will try to find the action in the controller. The following path works for me:

    match '*path', :to => 'errors#not_found'
    

    This won't catch a missing root path - but I'm guessing you're website is there.

    March 19th, 2011 @ 06:48 AM

  19. Andre Pankratz
    Andre Pankratz
    • Assigned user cleared.

    A catch-all route at the end of your routes my seem like a simple fix, but it has a major drawback:
    If your application relies on engines that extend your app with their own routes, things will break because those routes will never get fired!

    I've published a gem that solves the routing error. It basically catches the exception on Rack-level and re-raises it on application-level: https://github.com/vidibus/vidibus-routing_error

    March 19th, 2011 @ 08:36 AM

  20. Andre Pankratz
    Andre Pankratz
    • Assigned user set to José Valim

    Oops, I removed José. Sorry.

    March 19th, 2011 @ 08:37 AM

  21. Kevin Watt
    Kevin Watt

    I am using non-restful routes, which is perhaps why the catch-all doesn't work for me

    match ':controller(/:action(/:id))', :constraints => {:id => /.*/}
    

    March 19th, 2011 @ 02:36 PM