【问题标题】:root path for multiple controllers on rails routesrails 路由上多个控制器的根路径
【发布时间】:2012-04-14 20:23:55
【问题描述】:

我有两个资源控制器,我在其中使用 slug 来表示 ID。 (friendly_id 宝石)。

我可以在路由上显示一个资源的显示路径,但不能同时显示两个。即。

root :to => 'home#index'
match '/:id' => "properties#show"
match '/:id' => "contents#show"

基本上我想要这样的网址,

# Content
domain.com/about-us
domain.com/terms
# Property
domain.com/unique-property-name
domain.com/another-unique-property-name

无论我放在最上面的任何资源都有效。有没有办法做到这一点?

如果您能提供帮助,请提前致谢。

【问题讨论】:

标签: ruby-on-rails-3 ruby-on-rails-3.1


【解决方案1】:

这是未经测试的,但请尝试在您的路线上使用constraint

root :to => 'home#index'
match '/:id', :to => "properties#show",
              :constraints => lambda { |r| Property.find_by_id(r.params[:id]).present? }
match '/:id', :to => "contests#show",
              :constraints => lambda { |r| Contest.find_by_id(r.params[:id]).present? }

或者,您可以创建一个单独的类来响应matches?,而不是定义一个lambda proc。 (我建议将这些类放在单独的文件中,这些文件将在您的 Rails 应用程序中自动加载。)

# app/constraints/property_constraint.rb
class PropertyConstraint
  def self.matches?(request)
    property = Property.find_by_id(request.params[:id])
    property.present?
  end
end

# app/constraints/contest_constraint.rb
class ContestConstraint
  def self.matches?(request)
    contest = Contest.find_by_id(request.params[:id])
    contest.present?
  end
end

# config/routes.rb
root :to => 'home#index'
match '/:id', :to => "properties#show", :constraints => PropertyConstraint
match '/:id', :to => "contests#show", :constraints => ContestConstraint

不幸的是,这会导致额外的数据库查询(一次在路由中,一次在您的控制器中)。如果有人对减少这种情况有任何建议,请分享。 :)

【讨论】:

    【解决方案2】:

    这个 Rails 引擎做你想做的事:

    Slug Engine at Github

    基本上,作者的方法是在他的主应用程序中安装一个 Rails 引擎。该引擎包含一个用于处理存在的 slug 的控制器和一个用于过滤和弃权不存在的 slug 的中间件。

    他在一个相当详细和有趣的blog post 中解释了为什么他采用这种方法和其他中止的解决方案。这篇博文和 slug 引擎源代码应该足够详细,让您可以启动并运行自己的代码,但是如果您想要一个插入式解决方案,那么开源引擎似乎正是您正在寻找的。

    【讨论】:

      【解决方案3】:

      您可以编写另一个控制器,从路由器获取 id 并检查 id 是否属于属性或内容并呈现适当的视图。

      match '/:id' => "router#show"
      

      控制器会做这样的事情:

      def show
          @property = Property.find(params[:id])
          if @property then
              render 'property/show'
          else
              @content = Content.find(params[:id])
              render 'content/show
          end
      end
      

      尚未测试此代码,但这个想法应该可行。

      【讨论】:

      • 感谢惠斯勒。这是我采取的路线,但我认为从机架中间件中这样做会很好。
      【解决方案4】:

      如果可能,我建议您以更 RESTful 的方式执行此操作。基本上,你有两种不同的资源,你应该把它们分开:

      match 'properties/:id' => "properties#show"
      match 'contents/:id'   => "contents#show"
      

      这将为您带来许多优势。一个直接的好处是您可以避免属性和内容的 id 之间的冲突。 (请注意,friendly_id 不会帮助您解决原始方案中的模型间 slug 冲突。)

      【讨论】:

      • 你还有根吗?也许在下面?
      • 是的,当然这取决于您的应用程序应该如何工作。但很有可能。
      【解决方案5】:

      你可以在中间件中做到这一点

      1. 检测路径中的 slug
      2. 如果存在带有此 slug 的内容 - 将请求路径更改为“contents/:id”
      3. 如果存在带有此 slug 的属性 - 将请求路径更改为“properties/:id”
      4. 在您的路由集中:

        匹配 'contents/:id' => "properties#show"

        匹配 'properties/:id' => "contents#show"

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-16
        • 1970-01-01
        • 2020-08-30
        • 2016-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多