Devise 登録パラメータの形式が異なる場合があるのはなぜですか?
概要
Rails 5 アプリとの Devise 統合を少しカスタマイズしました。 Devise::RegistrationsController から継承し、Super を呼び出すのではなく、User リソースに対する独自の作成アクションを実装しています。
開発中は常に、本番環境ではほとんどの場合、フォームからのパラメータは次のように渡されます。
{
"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"
}
}
なぜこの 2 番目の形式のパラメーターが表示されるのかわかりません。私の推測では、状況によっては、Devise がパラメーターを異なる方法で処理するということです。私が検討できる唯一のケースは、検証の問題の後に再送信することですが、これを試行するたびに、パラメータは依然として最初の形式 (開発および運用環境) で渡されます。
create アクションは、params[:user] にユーザー属性のハッシュがあることを前提として記述されていますが、この 2 番目の形式には当てはまりません。これらのパラメーターの 2 つの形式を処理できますが、なぜこれが起こるのかを理解したいのですが、このアクションがランダムに異なるパラメーターで呼び出されるのは望ましくありません。
関連する場合は、送信フォーム (スタイル クラスは省略):
<%= 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] がある理由)と、その登録オブジェクトにラップされたパラメータ (JSON に対して Wrap_parameters を有効にしました)。
これでは説明できないのは、なぜ一部のリクエストだけがこのメディア タイプでタグ付けされるのかということです。
最終編集: ログをしばらく観察した結果、この問題はボットによって引き起こされていると結論付けました。障害は常に同じ IP からの 3 つのリクエストの一部として発生し、最後のリクエストのみが JSON データを持ち、すべて数秒以内に発生します。ボットはフォームを読み取り、Rails にとって意味のない方法で JSON に変換しているだけです。これを見てくださった方々に感謝します。
解決策
これは、ActionController::ParamsWrapper が開始されるときに発生します。
有効にすると、params ラッパーは、現在のコントローラーの名前でまだネストされていない場合、受信パラメーターをその名前でラップします。
この例では、user の下にネストされたパラメータを RegistrationsController という名前のコントローラに送信しています。したがって、ParamsWrapper はそれらを登録で再度ラップします。これらを UsersController に送信した場合、それらは既に user の下にネストされているため、変更にはなりません。
私見ですが、これは Ruby on Rails の最も役に立たず、エラーが発生しやすい魔法の動作の 1 つです。問題が発生することが多いため、私は通常すぐに無効にします。たとえば、フロントエンドはネストを行わず、この機能にのみ依存しているが、バックエンドは再構築して名前空間を導入するか、コントローラーの名前を変更する必要がある場合です。