动态URL – > Rails中路由的控制器映射

我希望能够根据数据库中的信息动态地将URL映射到控制器。

我正在寻找function相当于此的东西(假设View模型):

 map.route '/:view_name', :controller => lambda { View.find_by_name(params[:view_name]).controller } 

其他人建议动态重建路由 ,但这对我不起作用,因为可能有数千个视图映射到同一个Controller

这个问题很古老,但我发现它很有趣。 可以使用路由器路由到Rack端点的function在Rails 3中创建完全可用的解决方案。

创建以下Rack类:

  class MyRouter def call(env) # Matched from routes, you can access all matched parameters view_name= env['action_dispatch.request.path_parameters'][:view_name] # Compute these the way you like, possibly using view_name controller= 'post' my_action= 'show' controller_class= (controller + '_controller').camelize.constantize controller_class.action(my_action.to_sym).call(env) end end 

在路线

  match '/:view_name', :to => MyRouter.new, :via => :get 

提示从http://guides.rubyonrails.org/routing.html#routing-to-rack-applications中提到,“为了好奇,’post#index’实际上扩展为PostsController.action(:index),返回一个有效的Rack应用程序。“

在Rails 3.2.13中测试的变体。

所以我认为你问的是,如果你有一个View表和一个View模型,那么表就是这样的

 id | name | model =================== 1 | aaa | Post 2 | bbb | Post 3 | ccc | Comment 

你想要一个/ aaa的url指向Post.controller – 这是对的吗?

如果不是那么你的建议似乎很好,假设它的工作原理。

您可以将其发送到catch all操作并让操作查看url,运行find_by_name然后从那里调用正确的控制器。

 def catch_all View.find_by_name('aaa').controller.action end 

更新

您可以使用redirect_to甚至发送参数。 在下面的示例中,您将发送搜索参数

 def catch_all new_controller = View.find_by_name('aaa').controller redirect_to :controller => new_controller, :action => :index, :search => params[:search] end 

这是由zetetic和Steve ross贡献的一个很好的机架路由解决方案

使用rSpec测试机架路由

它向您展示了如何编写自定义调度程序(如果需要,您可以在其中执行数据库查找)以及约束和测试。

正如在单个应用程序中处理多个域的Rails路由问题中所建议的那样,我想你可以使用Rails Routing – Advanced Constraints来构建你需要的东西。

如果你有一个有限的控制器空间(无限的视图指向它们),这应该工作。 只需为每个控制器创建一个约束,以validation当前视图是否与它们匹配。

假设您有2个控制器(PostController和CommentController)的空间,您可以将以下内容添加到routes.rb:

 match "*path" => "post#show", :constraints => PostConstraint.new match "*path" => "comment#show", :constraints => CommentConstraint.new 

然后,创建lib / post_constraint.rb:

 class PostConstraint def matches?(request) 'post' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller } end end 

最后,创建lib / comment_constraint.rb:

 class CommentConstraint def matches?(request) 'comment' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller } end end 

您可以进行一些改进,例如定义获取缓存的超级约束类,这样您就不必重复代码,也不必冒险在其中一个约束中获取错误的缓存键名称。