Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can no longer rescue_from ActionController::RoutingError #671

Closed
lighthouse-import opened this issue May 16, 2011 · 58 comments
Closed

Can no longer rescue_from ActionController::RoutingError #671

lighthouse-import opened this issue May 16, 2011 · 58 comments
Labels

Comments

@lighthouse-import
Copy link

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse. Original ticket at: http://rails.lighthouseapp.com/projects/8994/tickets/4444
Created by Luigi Montanez - 2011-02-14 05:42:01 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Ben Marini - 2010-04-27 02:38:02 UTC

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?

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Jean - 2010-05-27 20:09:31 UTC

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)

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Neeraj Singh - 2010-05-28 03:35:17 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by José Valim - 2010-07-13 19:58:09 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Hubert Łępicki - 2010-07-25 16:47:51 UTC

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?

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Hubert Łępicki - 2010-07-25 16:52:04 UTC

I mean, you have to do it like this:

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

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Hubert Łępicki - 2010-07-26 07:14:54 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Matthew Gibbons - 2010-08-13 14:14:51 UTC

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
@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Andre Pankratz - 2010-08-17 17:11:44 UTC

Here is a gem that may solve this issue: http://github.com/vidibus/vidibus-routing_error

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by gucki - 2010-09-22 13:34:45 UTC

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

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

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by gucki - 2010-09-22 13:51:13 UTC

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

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Ryan Bigg - 2010-10-09 21:00:28 UTC

Automatic cleanup of spam.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Ryan Bigg - 2010-10-19 07:22:55 UTC

Automatic cleanup of spam.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Tian Davis - 2010-11-12 00:25:17 UTC

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-error-solution

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by avocade (at gmail) - 2010-12-19 02:50:57 UTC

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

http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Kevin Watt - 2011-03-18 15:40:52 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Kevin Watt - 2011-03-18 16:56:03 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Andrew White - 2011-03-19 06:48:59 UTC

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.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Andre Pankratz - 2011-03-19 08:36:52 UTC

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

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Andre Pankratz - 2011-03-19 08:37:41 UTC

Oops, I removed José. Sorry.

@lighthouse-import
Copy link
Author

@lighthouse-import lighthouse-import commented May 16, 2011

Imported from Lighthouse.
Comment by Kevin Watt - 2011-03-19 14:36:35 UTC

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

match ':controller(/:action(/:id))', :constraints => {:id => /.*/}
@josevalim
Copy link
Contributor

@josevalim josevalim commented May 16, 2011

Yeah, you can no longer do that. You need to define a catch all route.

@josevalim josevalim closed this May 16, 2011
@webmat
Copy link

@webmat webmat commented Jun 1, 2011

Why has this issue been closed? As far as I can tell, Rails 3.1 RC1 still hasn't added back support rescue_from, right?

Or is it going to be in the next RC / official release?

@josevalim
Copy link
Contributor

@josevalim josevalim commented Jun 1, 2011

No Exceptron did not get in Rails 3.1. :( We are going to merge it into master soon.

@rsanheim
Copy link
Contributor

@rsanheim rsanheim commented Jun 27, 2011

Should this issue be re-opened until the actual fix is merged?

@GAV1N
Copy link

@GAV1N GAV1N commented Jul 10, 2011

Any word on the status of this issue?

@albertoperdomo
Copy link
Contributor

@albertoperdomo albertoperdomo commented Jul 29, 2011

Can someone please provide some more info on the status of this issue? I'm still seeing it on edge.
Steps to reproduce: https://gist.github.com/1114757

@samuelkadolph
Copy link
Contributor

@samuelkadolph samuelkadolph commented Jul 29, 2011

With how rails 3 was restructured to be a real rack app, it's not longer easy and clean to raise that error inside the context of a controller instance.
The best approach is a catch all route at the end of routes.rb: `get "*" => "home#not_found". You could also route it to an action that would raise the error and then you could handle it with a rescue_from.

@albertoperdomo
Copy link
Contributor

@albertoperdomo albertoperdomo commented Jul 29, 2011

Ok, I understand it's going to stay this way.
Thanks for clarifying.

@moiristo
Copy link

@moiristo moiristo commented Aug 11, 2011

For those that are using gems that have their own routes: you can also add the catch-all route after initialize to account for that. Just put something like this in application.rb:

    # 404 catch all route
    config.after_initialize do |app|
      app.routes.append{ match '*a', :to => 'application#render_404' } unless config.consider_all_requests_local
    end
@pwim
Copy link
Contributor

@pwim pwim commented Dec 5, 2011

In case anyone else was like me, saw this bug, and decided to use exceptron: don't - it is abandoned.

@shearn89
Copy link

@shearn89 shearn89 commented Feb 15, 2012

Just to let fellow googlers know: this issue still appears to exist in rails 3.1.1, although I have yet to update to rails 3.2 in case of possible breakage.

@nunziofiore
Copy link

@nunziofiore nunziofiore commented Feb 20, 2012

I m in a big trouble and i think that this issue could be useful for me in some way, can somebody of you help me?

I used:

match '*a', :to => 'errors#e404'

(i tried also
config.after_initialize do |app|
app.routes.append{ match '*a', :to => 'application#render_404' } unless config.consider_all_requests_local
end
in application.rb but with no success)

and it works great but... I use omniauth and I have url like that

/auth/facebook for example

that are no listed in my gemfile...

I thought that @moiristo answer could help me but I have again an error because my

/auth/:service redirect to 404...

sorry for my english...-
-can you help me in some way?

@moiristo
Copy link

@moiristo moiristo commented Feb 20, 2012

had the exact same problem a while back. It's because the omniauth middleware is only executed after your rails app tells rack it couldn't process the request. So, I had to add a route to do just that:

match '/auth/:provider' => 'sessions#omniauth' 
  # Render a 404 to trigger the omniauth middleware  
  def omniauth
    render :text => '404', :status => 404
  end

you could probably also create a custom responder to handle this..

@nunziofiore
Copy link

@nunziofiore nunziofiore commented Feb 20, 2012

@moiristo you are the best, thank you very much
I added the match in application#omniauth
and the method in applicationcontroller
and it run perfectly

sincerely I didnt' understand very well why we had to add render :text => '404', :status => 404, but this is mine problem and I have to study it.
thank you again
Nunzio

@moiristo
Copy link

@moiristo moiristo commented Feb 20, 2012

It's because your rails app is a rack app as well. Rack calls every registered app until one handles it. This is what happens when you redirect. When you don't redirect and respond with 404, the next app in the chain is called. One of these apps is the omniauth middleware.

@nunziofiore
Copy link

@nunziofiore nunziofiore commented Feb 20, 2012

great explanation
Thank you very much
Nunzio Fiore

On Mon, Feb 20, 2012 at 11:40 AM, Reinier de Lange <
reply@reply.github.com

wrote:

It's because your rails app is a rack app as well. Rack calls every
registered app until one handles it. This is what happens when you
redirect. When you don't redirect and respond with 404, the next app in the
chain is called. One of these apps is the omniauth middleware.


Reply to this email directly or view it on GitHub:
#671 (comment)

@canma5
Copy link

@canma5 canma5 commented Feb 22, 2012

Hello,

I want to handle not only routing errors. I have used the code from #671 (comment) and is working fine.

What I would like to do is to render a page directly instead of displaying a message. Therefore I've replaced --> render(500, 'it was routing error') for this --> render :file => "#{RAILS_ROOT}/public/500.html", :status => 500

But I get an error: wrong number of arguments (1 for 2)

This is what im trying:

module ActionDispatch
class ShowExceptions
def render_exception(env, exception)
if exception.kind_of? ActionController::RoutingError
#render(500, 'it was routing error')
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
else
#render(500, "some other error")
render :file => "#{RAILS_ROOT}/public/500.html", :status => 500
end
end
end
end

Any help will be very welcome! Thanks in advance!

@moiristo
Copy link

@moiristo moiristo commented Feb 22, 2012

why don't you just use rescue_from for anything other than routing errors?

@canma5
Copy link

@canma5 canma5 commented Feb 23, 2012

Hi, I thought rescue_from didnt work in Rails 3.0.X versions. Reading now better the post I assume that the only one that doesnt work is for the Routing errors. Sorry, my bad!

@bkimble
Copy link

@bkimble bkimble commented Mar 28, 2012

@canma5 Correct - rescue_from doesn't work with ActionController::RoutingError in 3.2.0, but does work for everything else.

@jrochkind
Copy link
Contributor

@jrochkind jrochkind commented Apr 3, 2012

What is the current status?

11 months ago, josé:

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

10 months ago, josé:

No Exceptron did not get in Rails 3.1. :( We are going to merge it into master soon.

Now we're in several patch releases of rails 3.2. Is there any new official way to deal with this? Or is the only way still a catch-all route? The problem with the catch-all route, as others have mentioned, is it messes up engine-supplied routes. To deal with that, you can do it in an after_initialize block or some other initializer you hook in after engine routes are loaded... but this starts getting kind of cumbersome and confusing for the beginner. At one point José suggested Rails core team planned to implement some other fix -- is there still such a plan/desire?

@mattgreen
Copy link

@mattgreen mattgreen commented Apr 4, 2012

+1 looking forward to a status update.

@spencerfry
Copy link

@spencerfry spencerfry commented Apr 13, 2012

Any update on how to handle this?

@dmathieu
Copy link
Contributor

@dmathieu dmathieu commented Apr 24, 2012

This issue shouldn't be closed.

@guilleiguaran guilleiguaran reopened this Apr 24, 2012
@guilleiguaran
Copy link
Member

@guilleiguaran guilleiguaran commented Apr 30, 2012

Closing, see @spastorino comment

@jrochkind
Copy link
Contributor

@jrochkind jrochkind commented Apr 30, 2012

I do not understand how to apply @spastorino's comment. Is it possible to rescue from Routing exceptions in current Rails, and if so, how?

@josevalim
Copy link
Contributor

@josevalim josevalim commented Apr 30, 2012

For everyone waiting on a status update, I've written briefly about it here (point 3):

http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/

@jrochkind
Copy link
Contributor

@jrochkind jrochkind commented Apr 30, 2012

Thanks. Is rescue_from discouraged/deprecated behavior, and the new middleware-based config.exceptions_app should be used instead? Or just for routing errors?

@robertgrimm
Copy link

@robertgrimm robertgrimm commented Oct 19, 2012

Regarding the blog post that josevalim posted, is that the real way to handle this? I like that method and it works well for me except for integration tests that tests for a 404 server response since the ActiveRecord::RecordNotFound exception bubbles up to the tests. I've been able to fix that by doing the 'rescue_from ActiveRecord::RecordNotFound' thing but that seems like duplication since I also have a 'match 404' in my routes.

@phlegx
Copy link

@phlegx phlegx commented Dec 18, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.