We've got a rails app that keeps producing an error that we can't track down. The problem seems to lie with the routing but we're not sure what is happening. Out routes file looks like:
ActionController::Routing::Routes.draw do |map|
# Default
map.home '', :controller => "home"
# Admin
map.admin_home 'admin', :controller => 'admin/admin', :action => 'index'
map.admin_login 'admin/login', :controller => 'admin/admin', :action => 'login'
map.admin_reminder 'admin/forgot', :controller => 'admin/admin', :action => 'reminder'
map.namespace :admin do |admin|
admin.resources :bookings,
:collection => {
:archive => :get, :reports => :get, :calendar => :get,
:step_1 => :any, :step_1a => :any, :step_2 => :any, :step_3 => :any, :confirmation => :any }
admin.resources :events,
:member => { :status => :get }
admin.resources :blogs,
:collection => { :archive => :get }
admin.resources :blog_replies,
:member => { :publish => :get }
admin.resources :minutes,
:collection => { :archive => :get }
admin.resources :businesses
admin.resources :business_categories
admin.resources :users
admin.resources :pricings
admin.backups 'backups', :controller => 'admin/backups'
admin.download_backup 'backups/download', :controller => 'admin/backups', :action => 'download'
end
map.admin 'admin/:action', :controller => 'admin/admin'
map.connect 'members', :controller => 'admin/admin', :action => 'redirect_to_index'
map.connect 'members/login', :controller => 'admin/admin', :action => 'redirect_to_index'
map.connect 'account', :controller => 'admin/admin', :action => 'redirect_to_index'
map.connect 'account/login', :controller => 'admin/admin', :action => 'redirect_to_index'
map.connect 'home', :controller => 'admin/admin', :action => 'redirect_to_index'
map.connect 'home/login', :controller => 'admin/admin', :action => 'redirect_to_index'
map.blog 'blog/:permalink', :controller => 'blogs', :action => 'show'
map.connect 'blog/:id', :controller => 'blogs', :action => 'show'
map.connect 'book-online', :controller => 'bookings', :action => 'step_1'
map.connect 'book-online/:action', :controller => 'bookings'
# Defaults
map.connect ':controller/:action/:id'
map.connect "*anything", :controller => "public", :action => "unknown_request"
end
We have a set of public controllers in app/controllers and a set of admin controllers in app/controllers/admin. The issue we keep seeing is when a user goes to a url like admin/bookings, admin/bookings/step_1 or admin/events. Sometimes the urls work perfectly but other times I can see from the log file that something like the following happens:
ActionController::UnknownAction (No action responded to index):
Other times we'll get something like:
Processing EventsController#index (for [filtered] at 2009-01-21 10:54:38) [GET]
Session ID: [filtered]
Parameters: {"action"=>"index", "controller"=>"admin/events"}
Rendering template within layouts/public
Rendering events/index
Completed in 0.00863 (115 reqs/sec) | Rendering: 0.00338 (39%) | DB: 0.00000 (0%) | 200 OK [http://www.gresfordtrust.org/admin/events]
from the last example you can see that the url requested was admin/events which should have hit the #index in Admin::EventsController but instead it renders the #index action in EventsController.
We're running the app with Rails 2.0.2.
You don't have a configured route for EventsController, so your error is happening because some request is falling down to the default route, map.connect ':controller/:action/:id'
.
This is happening because someone/something is sending a request of an HTTP method that isn't configured for your AdminEventsController. Your admin.resources :events, :member => { :status => :get } will match the following requests:
GET /admin/events
GET /admin/events/<id>
GET /admin/events/<id>/status
POST /admin/events
PUT /admin/events/<id>
DELETE /admin/events/<id>
Anything else would fall through to the default route. So if you're seeing those ActionController::UnknownAction on this controller look for requests that are using the wrong HTTP method.
The source of your bizarre log message is almost certainly a request that was something like this:
GET /admin/events/index
The solution is to get rid of that default route entirely and ensure you're adding resource[s] routes for all the controllers in the right place.
Related
I have a Rails controller with about 60+ actions. I need to change it to allow only POST requests on about 20 actions and any request method for the rest of them.
Is there a way to do this so I don't have to manually specify ever route that is allowed for all routes?
This is what I have so far (and works):
post_regex = /first_route|second_route/
all_routes_regex = /third_route|fourth_route/
map.connect '/myroute/:id/:action', :controller => 'my_controller', :constraints => {:action => post_regex }, :conditions => { :method => :post }
map.connect '/myroute/:id/:action', :controller => 'my_controller', :constraints => {:action => all_routes_regex }
I tried creating something like this but it would just cause a RoutingError.
post_regex = /first_route|second_route/
class AllRoutesConstraint
def self.matches?(request)
(request.query_parameters[:action] !~ post_regex)
end
end
map.connect '/myroute/:id/:action', :controller => 'my_controller', :constraints => {:action => post_regex }, :conditions => { :method => :post }
map.connect '/myroute/:id/:action', :controller => 'my_controller', :constraints => {:action => AllRoutesConstraint }
If you are willing to do it in the controller instead of in routes.rb, it should be pretty straightforward. Let all request types through in the routes file:
# in config/routes.rb
map.connect '/myroute/:id/:action', :controller => 'my_controller'
And then, filter for POST-only actions in the controller.
# in app/controllers/my_controller.rb
POST_ONLY_ACTIONS = [:first_route, :second_route]
before_filter :must_be_post, :only => POST_ONLY_ACTIONS
# your actions...
protected
def must_be_post
unless request.method == "POST"
raise ActionController::MethodNotAllowed.new("Only post requests are allowed.")
end
end
This gets you the same error and error message that Rails would generate for you if you set the method in routes.rb.
The drawback is that your routes.rb file is no longer the single authoritative source on exactly what requests are permissible. But since you were trying to remove some of that information (the list of non-POST requests) from the routes file anyway, you might find the tradeoff acceptable.
Why do I have:
Unknown action
No action responded to 12345-yyyymmdd-6789-ABC-Another_Test. Actions: index, show, and sub_layout
url
localhost:3000/asset/recordings/12345-yyyymmdd-6789-ABC-Another_Test?time=00%3A25%3A05
Here is route.rb:
map.namespace :asset do |asset|
map.resources :asset, :controller => 'asset/recordings', :action => 'index' # index of asset
asset.resources :recordings, :controller => 'recordings'
asset.resources :recordings do |recording|
recording.resource :transcript
end
asset.resources :clip_lists, :controller => 'clip_lists'
map.delete_asset_clip_list 'asset/clip_list/:id/destroy', :controller => 'asset/clip_lists', :action => 'destroy'
map.asset_clip_list 'asset/clip_list/:id', :controller => 'asset/clip_lists', :action => 'show'
asset.connect ':asset/controller/:action/:id'
asset.connect ':asset/controller/:action/:id.:format'
end
If I add manually show?id= in the URL it works:
localhost:3000/asset/recordings/show?id=12345-yyyymmdd-6789-ABC-Another_Test?time=00%3A25%3A05
In my applications routes.rb I have defined three routes like the following
map.signup '/signup', :controller => 'users', :action => 'new'
map.login '/login', :controller => 'sessions', :action => 'new'
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
Is it possible for me to get the controller and action name for a particular path?
I am looking for some method like this...
def current_routes(a)
end
should return :controller => 'users', :action => 'new' if I call current_routes('signup_path')
Try like this
ActionController::Routing::Routes.recognize_path("/posts/")
If you only have a string with your route (like "signup_path"), then I guess in the context you're using this you should be able to do
ActionController::Routing::Routes.recognize_path(send("signup_path".to_sym))
I have the following routes defined:
map.resources :categories, :has_many => :downloads
map.resources :downloads, :member => {:go => :get}, :collection => {:tag => :get}
map.connect '/downlods/page/:page', :controller => 'downloads', :action => 'index'
map.connect '/categories/:category_id/downloads/page/:page', :controller => 'downloads', :action => 'index'
For some reason, the first page that the will_paginate helper is called on causes links with ?page=2 to be rendered, while subsequent pages have links with /downloads/page/2. Do you know what might be causing this?
If you simply declare a route with map.connect, it can be hit and miss as to how it's routed if you do something like:
link_to("Next", :page => 2)
What you might want to do is name the route and then use it that way:
map.downloads_paginated '/downloads/page/:page', :controller => 'downloads', :action => 'index'
Then you use the route by name:
link_to("Next", downloads_paginated_path(2))
These are much more reliable.
As a note, you have '/downlods' in your path instead of '/downloads' but I'm not sure that'd be causing the trouble described.
I use the will_paginate plug-in.
In oder to generate routes that I can cache ( /posts/index/2 instead of /posts?page=2) I added the following to my routes.rb:
map.connect '/posts/index/1', :controller => 'redirect', :url => '/posts/'
map.connect 'posts/index/:page',
:controller => 'posts',
:action => 'index',
:requirements => {:page => /\d+/ },
:page => nil
The first line redirects /posts/index/1 to /posts/ using a redirect controller, to avoid having a duplicate page.
Is there something wrong with the way I set up the 'posts/index/:page' rule?
I thought adding :requirements => {:page => /\d+/ } would ensure that /post/index/ without a :page parameter should not work, but /posts/index.html is getting cached.
How can I redirect /posts/index/ to /posts/ to avoid having both /posts.html and /posts/index.html ?
Thanks
UPDATE
I simply added
map.connect '/posts/index/', :controller => 'redirect', :url => '/posts/'
And I'm not getting duplicate pages anymore.
However, I still don't uderstand why I was getting /posts/index.html. Any explanations or suggestions on how to make this rule more succinct are welcome ;)!
map.connect '/posts/index/1', :controller => 'redirect', :url => '/posts/'
map.connect '/posts/index/', :controller => 'redirect', :url => '/posts/'
map.connect 'posts/index/:page',
:controller => 'posts',
:action => 'index',
:requirements => {:page => /\d+/ },
:page => nil
Here I found possible answer to your question.
I think that adding :page => nil can override previous condition. So maybe when you remove this part, it will work as you expected.