Rails 基于自定义 REST 方法生成的帮助程序路径失败



除了Rails默认定义的资源之外,还想将自定义路由定义为某些资源。 为此,我的routes.rb文件的相关部分如下所示:

resource :top, only: [:show]
scope module: :top do
  resource :reso, only: [:show]
end
get 'foo_reso' => 'top/reso#foo'
get 'bar_reso' => 'top/reso#bar'

如您所见,我只想要路由到 ResoController 的方法showfoobar 。 这有效,正如rake routes给出的那样:

    reso GET    /reso(.:format)           top/reso#show
foo_reso GET    /foo_reso(.:format)       top/reso#foo
bar_reso GET    /bar_reso(.:format)       top/reso#bar

这是有效的:单击应用程序中的链接,将您带到路由foo_reso确实会导致呼叫ResoController#foo,以及关联视图的后续显示。

但是,我认为路由定义只是有点丑陋,而不是定义路由显式,而是希望 Rails 通过告诉它资源有两个额外的 REST 方法自动生成它们,foobar(同时仍然通过 only: 参数限制标准方法(。

我遵循了这个答案中的建议,并将routes.rb改为:

resource :top, only: [:show]
scope module: :top do
  resource :reso, only: [:show] do
    member do
      get :foo
      get :bar
    end
  end
end

现在,rake routes给出:

    reso GET    /reso(.:format)           top/reso#show
foo_reso GET    /reso/foo(.:format)       top/reso#foo
bar_reso GET    /reso/bar(.:format)       top/reso#bar

请注意两种情况下路径之间的差异:虽然帮助程序路径和控制器#action相同,但路径已从/foo_reso(.:format)更改为/reso/foo(.:format)

(我在config/initializers/inflections.rb中将所有资源名称定义为不可数,所以我不会自动复数名称,因为在我的应用程序中,每个控制器都与特定屏幕相关联,而不是与模型相关联,因此复数不适合图片。在这个应用程序中,REST 方法实际上更像是函数调用,而不是对资源的操作,这就是为什么我需要与标准不同的集合。

现在,单击应用程序中foo_reso的链接会导致 Rails 路由错误页面,其中显示:

No route matches [GET] "/foo_reso"

除了使用我原来的解决方案之外,关于我可以做些什么来解决这个问题的任何想法?

添加编辑, 2013-07-12:

正如我在下面的评论中指出的那样,根据 rake routes 的输出,帮助程序路由foo_reso与我要调用的控制器和方法匹配,top/reso#foo,当我手动输入匹配的 URL ( reso/foo ( 到 URL 栏中时,它会按预期工作。 但是,尝试从应用程序内打开路由foo_reso会导致No route matches [GET] "/foo_reso"foo_reso_pathfoo_reso_url会导致相同的错误。

问题到底是什么? 肯定不是 Rails 中的错误吗?

2013-07-22 编辑添加:

为了使用例更清晰一些,这个想法是当用户按下页面上的"重置"按钮时,将调用控制器的reset方法,该方法清除页面的输入和输出。我一直通过将实际标识符reset更改为foo来抽象问题。(reso和其他几个标识符也是"混淆"的结果。 为了保持一致性,我将继续使用相同的翻译,但是当您记住foo实际上是reset时,下面的代码应该更清晰。(bar是别的,但没关系,因为路由问题是相同的。

为了回答Thong Kuah和John Hinnegan的问题,这就是我使用foo_reso_path的方式。 文件reso_controller的相关部分:

class Top::ResoController < Top::ResoSuperController
  # GET /reso
  def show
    ...
    @reset_path = "foo_reso"
    ...
  end # show
  # GET /foo_reso
  def foo
    perform_foo_action
    redirect_to reso_path
  end
  ...
end

文件app/views/top/reso/show.html.erb的相关部分:

<%= render partial: "top/resosuper_inputs", locals: { the_form_path: reso_path } %>
...
<input type="hidden" id="reset_path" name="reset_path" value="<%= @reset_path %>">

该表格包括部分_resosuper_inputs.html.erb,其相关部分是:

<!-- For using the "Reset" button. -->
<%= javascript_include_tag "my_reset_form.js" %>
<%= simple_form_for :filtering_criteria, url: the_form_path, method: :get do |f| %>
  ... inputs elided ...
  <%= f.button :submit, value: "Search" %>
  <%= f.button :submit, type: 'button', value: "Reset"), id: 'reset_button' %>
<% end %>

resosuper是两种不同资源的超类,但我认为这对案件没有任何影响。无论如何,这是Javascript文件app/assets/javascripts/my_reset_form.js

$(document).ready(function() {
  /* Hang functionality on the "Reset" button. */
  $('#reset_button').click(function () {
      var reset_path = $('#reset_path').val();
      window.open(reset_path, "_self")
  });
})

它在jQuery中,正如注释所说,使"重置"按钮打开其值已存储在隐藏输入变量中的路径,ID reset_path 。 该值是变量@reset_pathTop::ResoController赋予形式的值,即"foo_reso"

请记住,当我在routes.rb中定义foo_reso时,所有这些都工作正常,如下所示:

get 'foo_reso' => 'top/reso#foo'

另请记住,在 ResoController 的赋值语句中用 foo_reso_pathfoo_reso_url替换foo_reso没有区别。

我使用foo_reso的位置在控制器和视图中,所以这应该不是问题。

如描述中所述,这里是"用法"。

# GET /reso
def show
...
  @reset_path = "foo_reso"
...
end # show

但是,上述所做的只是将@reset_path实例变量设置为文字字符串"foo_reso"(巧合的是与旧路由匹配(

海报真正想要的是:

 @reset_path = foo_reso_path

这将生成正确的路径/reso/foo到实例变量@reset_path

旁注:海报在这里做了所有正确的事情来调试路由问题。大多数时候,您可以信任rake routes.检查是否可以直接访问路由是好的,因此检查路由助手的使用是否正确也至关重要

简短回答:在 Rails 中没有自动的方法可以做到这一点。

原因是 RESTul URL 基本上依赖于格式/controller/action 。这意味着如果你的控制器ResoController有 3 个方法showfoobar ,正确的 restful 实现将产生 URL:

/reso
/reso/foo
/reso/bar

Rails 可以帮助您实现这一目标,解决方案是在路线中使用members,与您在问题中描述的方式完全相同。

使用诸如/foo_reso/bar_reso之类的URL-换句话说/action_controller不是Rails的标准,而Rails的基本理念是"约定优于配置"。因此,如果您真的需要它们,则必须手动声明它们

好的,我发现了问题所在。在挖掘一个看似神秘的错误后,情况往往如此,解决方案非常简单。我reso_controller.rb是:

类 顶部::Reso控制器 <顶部::Reso超级控制器>

# GET /reso
def show
  ...
  @reset_path = "foo_reso"
  ...
end # show

我用这个替换了分配行:

  @reset_path = foo_reso_path

foo_reso_path周围没有引号. 现在,"重置"按钮按预期工作。

总之,以下方法不起作用

  @reset_path = "foo_reso"
  @reset_path = "foo_reso_path"
  @reset_path = "foo_reso_url"
  @reset_path = foo_reso

以下内容确实有效:

  @reset_path = foo_reso_path
  @reset_path = foo_reso_url

我现在不觉得傻吗。很抱歉浪费时间。

最新更新