はじめに
今回は、Railsで認証Gemをdeviseを使用したときに
ログアウトボタンを作成したときに,
DELETEリクエストがGETになっていたために
できませんでした。その時のメモを
開発環境
- Docker 27.3.1
- Ruby 3.3.6
- Rails 7.2.2
- PostgreSQL 17.2
解決方法
$ yarn add @rails/ujs
import Rails from "@rails/ujs"; #この2行追加
Rails.start();
$ docker compose up
$ docker compose down
これでログアウトボタンをDELETEでいけます。
<%= link_to 'ログアウト', destroy_user_session_path, method: :delete, data: { turbo: false } %>
Turboについて
Rails7以降でTurboが使用されTurboはフォームやリンクを AJAX 的に処理するため、通常のリクエストが無効化されることあります。ログアウトリンクで Turbo を無効にするため、次のようにdata: { turbo: false }をログアウトボタンに記載します。
@rails/ujs が動作する仕組み
ページロード時に @rails/ujs がHTMLを監視。
data-method 属性を持つリンクがクリックされた場合、JavaScript で動的に
これにより、ブラウザが直接サポートしていない DELETE メソッドでもサーバーにリクエストを送ることができます
なぜ@rails/ujsが必須なのか?
@rails/ujs を読み込まないと、data-method 属性を処理するJavaScriptが動作しないため、リンクをクリックしてもただの通常のリンク(GET リクエスト)として扱われます。
この結果、Railsのルーティングで期待されるHTTPメソッドが一致せず、エラーになります。
例
以下のコードで @rails/ujs がない場合の動作と、ある場合の動作を比較します。
<%= link_to "Logout", destroy_user_session_path, method: :delete %>
生成されるHTML
<a rel="nofollow" data-method="delete" href="/users/sign_out">Logout</a>
@rails/ujs がない場合
ブラウザは data-method を無視。
GET /users/sign_out がサーバーに送信される。
RailsはDELETE /users/sign_outを期待しているため、エラーが発生。
@rails/ujs がある場合
data-method="delete" を検出。
JavaScriptが自動的に
Railsは正しいHTTPメソッドを受け取るため、ログアウト処理が正常に行われる。