Ruby on Rails选择错误的控制器动作

Mik*_*ike 6 rails-routing ruby-on-rails-4

今天我遇到了一些奇怪的(并且非常不方便)Ruby on Rails行为,即使持续梳理网络也没有产生令人满意的答案.注意:我将方法和路由名称翻译为更容易用英语阅读,并希望我没有引入任何不一致.

情况

环境

Ruby on Rails 4.2.0在Ruby 2.0下执行(也在Ruby 2.2.0下测试)

相关守则

考虑使用这些操作的控制器,其中包括:

class AssignmentsController < ApplicationController
  def update
    ...
  end

  def takeover_confirmation
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

的routes.rb

自从我用了很多手工定义路线的,我也没有在routes.rb中使用的资源.有问题的路线定义如下:

...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
Run Code Online (Sandbox Code Playgroud)

相关产出rake routes:

assignment POST  /assignments/:id(.:format)  assignments#update
assignments_takeover_confirmation  POST  /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
Run Code Online (Sandbox Code Playgroud)

问题

当我assignments_takeover_confirmation_path对它进行POST时,rails会将其路由到该update方法.服务器日志:

Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
Run Code Online (Sandbox Code Playgroud)

减轻

如果我把update路由定义takeover_confirmation一个,它按预期工作(没有检查的支撑柱,update虽然).

此外,写完所有这些之后我发现我update在routes.rb(POST而不是PATCH)中使用了错误的请求类型.在routes.rb中这样做确实解决了我的问题:

patch 'assignments/:id' => 'assignments#update', as: 'assignment'
Run Code Online (Sandbox Code Playgroud)

但是,即使将其定义为POST,Rails也不应该将POST请求指向现有路径"/ assignments/takeover_confirmation"到完全不同的操作,如果它?我担心下次我为同一个控制器使用两个POST路由时,它会再次做同样的事情.

我似乎对Rails路由有一种严重的误解,但不能指责它......

编辑:解决方案

正如katafrakt解释的那样,上面的请求/assignments/takeover_confirmation与路由匹配,assignments/:id因为Rails将"takeover_confirmation"部分解释为字符串并将其用于:id参数.因此,这是完全预期的行为.

工作实例

为了完整起见,这里有一个工作(如果是简约的)路线定义,它应该受到Chris的评论的启发:

  resources :assignments do
    collection do
      post 'takeover_confirmation'
    end
  end
Run Code Online (Sandbox Code Playgroud)

在此示例中,仅显式定义了我手动创建的路由.更新,显示等的路由(我最初手动定义)现在由隐式定义resources: :assignments.

相应摘录自rake routes:

...
takeover_confirmation_assignments  POST  /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
...
assignment GET    /assignments/:id(.:format)  assignments#show
           PATCH  /assignments/:id(.:format)  assignments#update
           PUT    /assignments/:id(.:format)  assignments#update
           DELETE /assignments/:id(.:format)  assignments#destroy
....
Run Code Online (Sandbox Code Playgroud)

谢谢您的帮助!

kat*_*akt 7

但是,即使将其定义为POST,Rails也不应该将POST请求指向现有路径"/ assignments/takeover_confirmation"到完全不同的操作,如果它?

这应该.Rails路由的匹配顺序与routes.rb文件中定义的顺序完全相同(从上到下).因此,如果它匹配某个规则(并/assignments/takeover_confirmation匹配assignments/:id规则),它将停止处理路由.

这种行为简单而有效.我认为任何一种匹配最佳路线的"智能"都会导致繁琐而意想不到的结果.

BTW这就是为什么catch-all路由曾经在路由文件的最底部定义的原因.