views:

1108

answers:

2

So. I'm playing with the routing.rb code in Rails 2.1, and trying to to get it to the point where I can do something useful with the RoutingError exception that is thrown when it can't find the appropriate path.

This is a somewhat tricky problem, because there are some class of URLs which are just plain BAD: the /azenv.php bot attacks, the people typing /bar/foo/baz into the URL, etc... we don't want that.

Then there's subtle routing problems, where we do want to be notified: /artists/ for example, or ///. In these situations, we may want an error being thrown, or not... or we get Google sending us URLs which used to be valid but are no longer because people deleted them.

In each of these situations, I want a way to contain, analyze and filter the path that we get back, or at least some Railsy way to manage routing past the normal 'fallback catchall' url. Does this exist?

EDIT:

So the code here is:

     # File vendor/rails/actionpack/lib/action_controller/rescue.rb, line 141
   def rescue_action_without_handler(exception)
     log_error(exception) if logger
     erase_results if performed?

     # Let the exception alter the response if it wants.
     # For example, MethodNotAllowed sets the Allow header.
     if exception.respond_to?(:handle_response!)
       exception.handle_response!(response)
     end

     if consider_all_requests_local || local_request?
       rescue_action_locally(exception)
     else
      rescue_action_in_public(exception)
     end
   end

So our best option is to override log_error(exception) so that we can filter down the exceptions according to the exception. So in ApplicationController

  def log_error(exception)
      message = '...'
      if should_log_exception_as_debug?(exception)
        logger.debug(message)
      else
        logger.error(message)
      end
  end

  def should_log_exception_as_debug?(exception)
     return (ActionController::RoutingError === exception)
  end

Salt for additional logic where we want different controller logic, routes, etc.

+1  A: 

There's the method_missing method. You could implement that in your Application Controller and catch all missing actions, maybe logging those and redirecting to the index action of the relevant controller. This approach would ignore everything that can't be routed to a controller, which is pretty close to what you want.

Alternatively, I'd just log all errors, extract the URL and sort it by # of times it occured.

MattW.
Using method_missing could cause a number of problems. For one, if there is a mis-labeled before_filter that's used for security, method_missing will silently capture the call and you end up with an routing error in your log and someone can view an admin page when they shouldn't be able to.
epochwolf
+4  A: 

Nooooo!!! Don't implement method_missing on your controller! And please try to avoid action_missing as well.

The frequently touted pattern is to add a route:

map.connect '*', :controller => 'error', :action => 'not_found'

Where you can show an appropriate error.

Rails also has a mechanism called rescue_action_in_public where you can write your own error handling logic -- we really should clean it up and encourage people to use it. PDI! :-)

0124816
The problem is that rescue_action_in_public doesn't prevent the logging of the error -- the code goes:141: def rescue_action_without_handler(exception)142: log_error(exception) if logger...151: if consider_all_requests_local || local_request?152: rescue_action_locally(exception)153: else154: rescue_action_in_public(exception)155: end156: end
Will Sargent