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:手段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 

请注意两种情况下路径之间的差异:辅助路径和控制器#操作相同时,路径已从/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方法,从而清除页面的输入和输出。 我一直在通过将实际标识符resetfoo来抽象问题。 ( 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的相关部分:

  ... <input type="hidden" id="reset_path" name="reset_path" value=""> 

该表单包含部分_resosuper_inputs.html.erb ,其相关部分为:

    ... inputs elided ...    

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 。 该值是Top::ResoController在变量@reset_path中给表单Top::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实现将导致URLS:

 /reso /reso/foo /reso/bar 

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

使用诸如/foo_reso/bar_reso – 换句话说/action_controller不是Rails标准,而Rails基础哲学是“约定优于配置”。 所以,如果你真的需要那些,你必须手工申报

好的,我发现了问题。 在深入研究一个看似神秘的bug后经常会出现这种情况,解决方案非常简单。 我的reso_controller.rb是:

class Top :: ResoController

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

我用这个替换了赋值行:

  @reset_path = foo_reso_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 

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