我有一个稍微定制的Devise与Rails 5应用程序的集成。我继承了Devise::RegistrationsController
,并为User
资源实现了自己的create
操作,而不是调用super
。
在开发的所有时间里,以及在生产的大部分时间里,表单中的params
都是这样的:
{
"utf8"=>"✓",
"authenticity_token"=>"***",
"user"=> {
"email"=>"[email protected]",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"
},
"commit"=>"Create account"
}
字符串
但在少数情况下,仅在生产环境中,params
看起来像这样:
{
"utf8"=>"✓",
"authenticity_token"=>"***",
"user[email]"=>"[email protected]",
"user[password]"=>"[FILTERED]",
"user[password_confirmation]"=>"[FILTERED]",
"commit"=>"Create account",
"registration"=> {
"user[email]"=>"[email protected]",
"user[password]"=>"[FILTERED]",
"user[password_confirmation]"=>"[FILTERED]",
"commit"=>"Create account"
}
}
型
我不知道为什么会出现第二种形式的参数。我的假设是,在某些情况下,Devise会以不同的方式处理参数。我能想到的唯一一种情况是在验证问题后重新提交,每次我尝试这样做时,参数仍然以第一种形式出现(在开发和生产中)。create
操作是在假设params[:user]
有一个用户属性散列的情况下编写的,而第二种形式的情况并非如此。我可以处理这两种形式的参数,但我想了解为什么会发生这种情况,我不希望这个操作被随机不同的参数调用。
如果它是相关的,提交表单(省略样式类):
<%= form_for(@user, as: :user, url: registration_path(:user)) do |f| %>
<div class="row">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true %>
</div>
<div class="row">
<%= f.label :password %>
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="row">
<%= f.label :password_confirmation, 'Password confirmation' %>
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="row">
<div>
<%= f.submit "Create account" %>
</div>
</div>
<% end %>
型
额外的日志记录证实了一些有趣的事情:被窃听的请求的request.media_type
是application/json
(典型的请求应该是application/x-www-form-urlencoded
在我看来,表单数据是用错误的请求类型提交的,这可能解释了为什么值没有被解包(为什么我们在参数中使用user[email]
作为属性名)以及为什么我们在registration
对象中 Package 参数(我为JSON启用了wrap_parameters)。
这并不能解释为什么只有一些请求被标记为这种媒体类型。
1条答案
按热度按时间zour9fqk1#
这发生在
ActionController::ParamsWrapper
生效时。启用后,params Package 器将使用当前控制器的名称 Package 传入的参数(如果尚未嵌套在该名称下)。
在您的示例中,您将嵌套在
user
下的参数发送到名为RegistrationsController
的控制器。因此,ParamsWrapper再次使用registration
Package 它们。如果您将它们发送到UsersController
,它们将不会更改,因为它们已经嵌套在user
下。恕我直言,这是Ruby on Rails最无用和最容易出错的神奇行为之一。我通常会立即禁用它,因为它经常会导致问题。例如,当前端不做任何嵌套,只是依赖于此功能,但后端需要重新构造并引入名称空间或重命名控制器时。