This project is archived and is in readonly mode.
AbstractController::ActionNotFound should be rescued
Reported by Kenn Ejima | January 10th, 2011 @ 10:33 PM
Suppose I have the following routes.rb:
match ':controller/:action', :controller => /api\/(room|user)/
match 'api/*others', :to => 'api/base#render_404_as_json'
and controllers:
class Api::BaseController
def render_404_as_json
...
render :text => '{"status":"error","code":"invalid_api_method"}'
end
end
class Api::RoomController < Api::BaseController
end
class Api::UserController < Api::BaseController
end
Now, when "/api/bogus" gets hit, the request is routed to
render_404_as_json
as it should.
However, when "/api/room/bogus" gets hit, it raises
AbstractController::ActionNotFound
, even if I had the
following lines in api/base_controller.rb or
application_controller.rb.
rescue_from Exception, :with => :render_500_as_json
rescue_from AbstractController::ActionNotFound, :with => :render_404_as_json
My goal here is to never return an unstructured HTML response of 404 for any URL under /api.
I think we have two ways to get around this:
- AbstractController::ActionNotFound should be raised inside RoomController so that you can catch it and do your own stuff, or
- When AbstractController::ActionNotFound is raised, continue down the routing matches (in the example above, 'api/*others' in the next line matches)
Comments and changes to this ticket
-
Andrew White February 9th, 2011 @ 04:24 AM
- State changed from new to duplicate
- Importance changed from to Low
This is essentially a duplicate of #4444. You've almost got the workaround from that ticket but your problem is that the first route is partially matching on the controller. If you add an :action regexp constraint listing the valid actions it should fall through to the second route.
-
Kenn Ejima February 23rd, 2011 @ 10:16 PM
Thanks Andrew, patching
render_exception
worked as you said. In my case:require 'action_dispatch/middleware/show_exceptions' module ActionDispatch class ShowExceptions private def render_exception_with_template(env, exception) if exception.kind_of? AbstractController::ActionNotFound request = Request.new(env) if request.env['action_dispatch.request.path_parameters'][:controller] =~ /^api\/.+/ return Api::BaseController.action(:render_404_as_json).call(env) end end render_exception_without_template(env, exception) end alias_method_chain :render_exception, :template end end
I have 20 controllers and 100+ actions under the /api context so listing them all in the :action regexp constraint seemed like a bad idea.
-
Kevin Watt March 19th, 2011 @ 01:35 AM
I'm trying your solution, but don't seem to have access to MyApp::HomeController in this context. Any tips? Is it in any strange namespaces besides the one I define in application.rb?
-
Andrew White March 19th, 2011 @ 06:36 AM
Shouldn't it be in the global namespace? i.e. HomeController and not MyApp::HomeController.
-
Kevin Watt March 19th, 2011 @ 02:43 PM
That fixed it, thanks. When rendering your action you can't re-raise and expect that to get caught like normal, since it's not being wrapped in the begin block that does the rescue_from handling in the first place.
I wish there was a way to have my web server (litespeed, but shouldn't matter) to see that it got a status 404 from the backend handler, then render the standard error page.
Though routing errors still cause a 500 rather than 404 which sucks.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>