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
的方法show
, foo
和bar
。 这有效,因为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方法foo
和bar
来自动生成它们(同时仍然限制标准方法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_path
和foo_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
的相关部分:
... <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_path
或foo_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个方法show
, foo
和bar
,则正确的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