Ruby Rails 选择了错误的控制器操作
Ruby on Rails choosing wrong controller action
今天,我在 Rails 上遇到了一些奇怪的(而且非常不方便)Ruby 行为,即使持续梳理网络也没有得到令人满意的答案。
注意:我将方法和路由名称翻译成英文更容易阅读,希望我没有引入任何不一致的地方。
情况
环境
Ruby on Rails 4.2.0 在 Ruby 2.0 下执行(也在 Ruby 2.2.0 下测试)
相关代码
考虑具有以下操作的控制器,其中包括:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
因为我使用了很多手动定义的路由,所以我没有使用routes.rb中的资源。有问题的路由定义如下:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
rake routes
的相关输出:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
问题
当我对 assignments_takeover_confirmation_path
执行 POST 时,rails 将其路由到 update
方法。服务器日志:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
缓解
如果我将 update
路由定义 放在 takeover_confirmation
之后 ,它会按预期工作(没有检查 POST update
虽然)。
此外,在写完所有这些之后,我发现我在 routes.rb 中对 update
方法使用了错误的请求类型(POST 而不是 PATCH)。在 routes.rb 中这样做确实解决了我的问题:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
然而,即使将其定义为 POST,Rails 也不应将对现有路径“/assignments/takeover_confirmation”的 POST 请求指向完全不同的操作,应该是?
我担心下次我为同一个控制器使用两条 POST 路由时,它会再次做同样的事情。
看来我对 Rails 路由有严重的误解,但我不能指手画脚...
编辑:解决方案
正如 katafrakt 解释的那样,上面对 /assignments/takeover_confirmation
的请求与路由 assignments/:id
匹配,因为 Rails 将 "takeover_confirmation" 部分解释为字符串并将其用于 :id 参数.因此,这是完全预期的行为。
工作示例
为了完整起见,这里是一个工作的(如果是简约的)路由定义,它应该按照 Chris 的评论的启发:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
在这个例子中,只有我手动创建的路由被显式定义。 update、show 等路由(我最初手动定义的)现在由 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
....
感谢您的帮助!
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
应该。 Rails 路由的匹配顺序与 routes.rb
文件中定义的顺序完全相同(从上到下)。因此,如果它匹配某个规则(并且 /assignments/takeover_confirmation
匹配 assignments/:id
规则),它将停止处理路由。
这种行为简单高效。我想任何类型的 "smart" 匹配最佳路线都会导致麻烦和意外的结果。
顺便说一句,这就是为什么过去常常在路由文件的最底部定义 catch-all 路由。
今天,我在 Rails 上遇到了一些奇怪的(而且非常不方便)Ruby 行为,即使持续梳理网络也没有得到令人满意的答案。 注意:我将方法和路由名称翻译成英文更容易阅读,希望我没有引入任何不一致的地方。
情况
环境
Ruby on Rails 4.2.0 在 Ruby 2.0 下执行(也在 Ruby 2.2.0 下测试)
相关代码
考虑具有以下操作的控制器,其中包括:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
因为我使用了很多手动定义的路由,所以我没有使用routes.rb中的资源。有问题的路由定义如下:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
rake routes
的相关输出:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
问题
当我对 assignments_takeover_confirmation_path
执行 POST 时,rails 将其路由到 update
方法。服务器日志:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
缓解
如果我将 update
路由定义 放在 takeover_confirmation
之后 ,它会按预期工作(没有检查 POST update
虽然)。
此外,在写完所有这些之后,我发现我在 routes.rb 中对 update
方法使用了错误的请求类型(POST 而不是 PATCH)。在 routes.rb 中这样做确实解决了我的问题:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
然而,即使将其定义为 POST,Rails 也不应将对现有路径“/assignments/takeover_confirmation”的 POST 请求指向完全不同的操作,应该是? 我担心下次我为同一个控制器使用两条 POST 路由时,它会再次做同样的事情。
看来我对 Rails 路由有严重的误解,但我不能指手画脚...
编辑:解决方案
正如 katafrakt 解释的那样,上面对 /assignments/takeover_confirmation
的请求与路由 assignments/:id
匹配,因为 Rails 将 "takeover_confirmation" 部分解释为字符串并将其用于 :id 参数.因此,这是完全预期的行为。
工作示例
为了完整起见,这里是一个工作的(如果是简约的)路由定义,它应该按照 Chris 的评论的启发:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
在这个例子中,只有我手动创建的路由被显式定义。 update、show 等路由(我最初手动定义的)现在由 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
....
感谢您的帮助!
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
应该。 Rails 路由的匹配顺序与 routes.rb
文件中定义的顺序完全相同(从上到下)。因此,如果它匹配某个规则(并且 /assignments/takeover_confirmation
匹配 assignments/:id
规则),它将停止处理路由。
这种行为简单高效。我想任何类型的 "smart" 匹配最佳路线都会导致麻烦和意外的结果。
顺便说一句,这就是为什么过去常常在路由文件的最底部定义 catch-all 路由。