【问题标题】:How can I route to different controllers with the same URL format in Rails?如何在 Rails 中路由到具有相同 URL 格式的不同控制器?
【发布时间】:2011-01-24 13:16:32
【问题描述】:

我有两个不同的模型可以显示在一个类别中。在我的 routes.rb 中,我想要这样的东西:

ActionController::Routing::Routes.draw do |map|
  map.top_list ':category/:foo', :controller => 'foo', :action => 'show'
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end

当我加载像“/some-category-name/some-foo-name”这样的 URL 时,这可以正常工作,其中“some-foo-name”可以由 FooController 加载,如下所示:

class FooController < ApplicationController
  def show
    @foo = Foo.find_by_url! params[:foo]
  end
end

但是当我尝试请求一个栏时,例如“/some-category-name/some-bar-name”,我得到一个“ActiveRecord::RecordNotFound in FooController#show”。我知道我可以通过要求所有 Foo 名称以“foo-”开头并且所有 Bar 名称以“bar-”开头来解决这个问题,然后定义如下路由:

ActionController::Routing::Routes.draw do |map|
  map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => /^foo-/ }
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show', :requirements => { :name => /^bar-/ }
end

但是强制对名称进行这种限制是非常不理想的。我发现了以下内容,看起来它可能对我有用:Different routes but using the same controller for model subclasses in Rails。但是,我并没有完全遵循这个例子,所以我不知道这是否能解决我的问题。我的 Foo 和 BarController 必须从 CategoryController 继承也不是很好。

我想到的一个想法是,我可以尝试查找 Foo,然后如果失败则回退到 BarController。我可以轻松地在 FooController 中执行此操作并重定向到 BarController,但这并不是真的可以,因为所有对 Bars 的请求都将在 Rails 日志中记录为 FooController#show。但是,如果我可以以某种方式配置路由以调用方法来根据 URL 的基本名称确定路由到什么,我可以获得我需要的行为;例如

ActionController::Routing::Routes.draw do |map|
  is_a_foo = Proc.new {|name| Foo.find_by_url! name && true }

  map.top_list ':category/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
  map.top_list ':category/:bar', :controller => 'bar', :action => 'show'
end

在标准 Rails 2 中有什么方法可以做到这一点吗?

【问题讨论】:

标签: ruby-on-rails url-routing


【解决方案1】:

我最终编写了一个小 Rails 插件,它允许我编写这样的路线:

map.with_options :category => /[-A-Za-z0-9]+/ do |m|
  # Since both foos and bars can appear under category, we define a route for foo that only matches when
  # the foo can be found in the database, then fall back to the bar route
  m.foo ':category/:foo', :controller => 'foo', :action => 'show', :conditions => {
      :url => { :find_by_url => Foo }
  }
  m.bar ':category/:bar', :controller => 'bar', :action => 'show'
end

在我获得开源代码的许可之前,我必须将实现留给读者作为练习,但我是这样实现的:

  1. Monkey-patching Rails: Extending Routes #2
  2. Under the hood: route recognition in Rails
  3. The Complete Guide to Rails Plugins: Part II

我会尽可能用 github 链接更新这个答案。 :)

【讨论】:

    【解决方案2】:

    这似乎是错误的,因为您使用相同的路由到两个控制器。 Rails 只会选择你注意到的一个。

    使用这样的东西会更好:

    map.top_list ':category/foo/:foo', :controller => 'foo', :action => 'show', :requirements => { :name => is_a_foo }
    map.top_list ':category/bar/:bar', :controller => 'bar', :action => 'show'
    

    因为在提供的路线中,您每次都提供两个参数。以不同的方式命名它们并不重要。它只是匹配两者。

    【讨论】:

    • 我同意,这样会很好。但是,路径是业务人员设定的要求。 :)
    • 所以给这些路径一些例子,我明天早上上班看看,这样我们就可以弄清楚了。可能有两种可能性: 1. 您将不得不使用一个控制器,它会稍作调整,但前提是 :foo 和 :bar 标识符是唯一的,例如您可以使用 before_filter 2. 至少有一个很大的可能性如果标识符可能重叠,路径必须略有不同。
    猜你喜欢
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    • 2019-09-21
    • 2014-10-07
    相关资源
    最近更新 更多