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

Posted

技术标签:

【中文标题】动态 URL -> Rails 中路由的控制器映射【英文标题】:Dynamic URL -> Controller mapping for routes in Rails 【发布时间】:2011-02-05 11:33:19 【问题描述】:

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

我正在寻找功能与此等效的东西(假设是View 模型):

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

其他人建议dynamically rebuilding the routes,但这对我不起作用,因为可能有数千个视图映射到同一个控制器

【问题讨论】:

控制器不需要由数据库记录来确定,我只是想要一些方法来评估在运行时而不是设计时为给定路由使用哪个控制器。 仍然没有找到解决方案,尽管这似乎是一项简单的任务。也许是一个重写 URL 的 Rack-Application? 【参考方案1】:

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

创建以下 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 的提示“对于好奇的人,'posts#index' 实际上扩展为 PostsController.action(:index),它返回一个有效的 Rack 应用程序。”

在 Rails 3.2.13 中测试的变体。

【讨论】:

【参考方案2】:

所以我认为您是在问,如果您有一个 Views 表和一个 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

【讨论】:

如何“调用正确的控制器”? Rails 维护每个控制器的实例并设置请求上下文、填充 params 和许多其他内容,我如何指示 rails 将请求从一个控制器内转发到另一个控制器? redirect_to 将向客户端发送302 HTTP 响应,导致浏览器重新请求新的 URL。这不是我们想要的。 您曾经能够在 pre rails 2.3 中使用 render_component 但如果您仍然想这样做,请查看github.com/rails/render_component 插件 "redirect_to" 看起来不像问题的答案【参考方案3】:

这是一个不错的 SEO 机架路由解决方案,由 zetetic 和 Steve ross 提供

Testing Rack Routing Using rSpec

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

【讨论】:

【参考方案4】:

正如问题Rails routing to handle multiple domains on single application 中所建议的那样,我想您可以使用Rails Routing - Advanced Constraints 来构建您需要的东西。

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

假设您有 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

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

【讨论】:

以上是关于动态 URL -> Rails 中路由的控制器映射的主要内容,如果未能解决你的问题,请参考以下文章

在 Rails 中将页面路由到没有额外后缀的 url

前端权限控制

6--Rails架构中的资源2

Rails URL 的嵌套路由和参数(最佳实践)

MVC:Home-Controller 中的动态路由,不影响其他控制器

Rails 6 - 将 URL 中 root_path 的参数更改为 SEO 友好的 URL