はじめに
今回は「診断する」ボタンを押したときに発生したエラーについて。解決までの流れを記事にまとめてみました。
環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3
エラー状況
1つめのエラー
application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:4026 Error: Form responses must redirect to another location
at _FormSubmission.requestSucceededWithResponse (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1641:22)
at FetchRequest.receive (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1389:21)
at FetchRequest.perform (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1367:25)
2つめのエラー
new:1 Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
調べてみるとどちらも「Rails アプリケーションの JavaScript とフォームの送信に関連」しているそうです。
1つ目のエラー: Form responses must redirect to another location
内容:
このエラーは、フォームを送信した後に、Rails が適切にリダイレクトを行わなかった場合に発生するもので、通常、Rails のフォーム送信は、成功した後に別のページにリダイレクトされる必要があります。
エラー原因
-
「診断する」ボタンの押下後、リダイレクトがないことフォームが送信された後、ページは通常
POST
リクエストを送信し、結果を表示するために別の場所にリダイレクトする必要があります。現在、フォームはPOST /posts
を実行していますが、結果を表示するためのリダイレクト処理が不足している可能性がある状態!
👇こちらの記事を参考にしつつも手探りで。
解決策:postsコントローラー
-
リダイレクトを追加する
create
アクションを追加し、フォームが送信された後、診断結果を表示するindex
アクションにリダイレクトするように変更します
変更前
class PostsController < ApplicationController
def new
# フォームを表示するためのアクション
end
def index
# パラメータを取得
question1 = params[:question1]
question2 = params[:question2]
question3 = params[:question3]
question4 = params[:question4]
question5 = params[:question5]
# 「はい」(1)の数を合計
yes_count = [question1, question2, question3, question4, question5].map(&:to_i).sum
# 結果に基づいて表示する内容を決定
@result = yes_count
# 診断結果用にすべてのPostレコードを取得
@posts = Post.all
end
end
変更後
class PostsController < ApplicationController
def new
# 新規フォームを表示
end
def create
# フォーム送信後の処理
redirect_to posts_path(
question1: params[:question1],
question2: params[:question2],
question3: params[:question3],
question4: params[:question4],
question5: params[:question5]
)
end
def index
# パラメータを取得
question1 = params[:question1]
question2 = params[:question2]
question3 = params[:question3]
question4 = params[:question4]
question5 = params[:question5]
# 「はい」(1)の数を合計
yes_count = [question1, question2, question3, question4, question5].map(&:to_i).sum
# 結果に基づいて表示する内容を決定
@result = yes_count
# 診断結果用にすべてのPostレコードを取得
@posts = Post.all
end
end
解決策:ルーティング
変更前
# トップページ
root 'tops#index'
# 診断フォームを表示するルート
get 'posts/new', to: 'posts#new', as: :new_post
# 診断結果を表示するためのルート
post 'posts', to: 'posts#index', as: :posts
変更後
# トップページ
root 'tops#index'
# 診断フォームを表示するルート
get 'posts/new', to: 'posts#new', as: :new_post
# 診断結果を表示するためのルート
post 'posts', to: 'posts#create', as: :posts
# 診断結果ページ
get 'posts', to: 'posts#index', as: :result_posts
-
root 'tops#index'
: トップページはtops#index
アクションを表示します。 -
get 'posts/new', to: 'posts#new', as: :new_post
: 診断フォームのページです(/posts/new
)。 -
post 'posts', to: 'posts#create', as: :posts
: 診断フォームの送信先です(POST /posts
)。 -
get 'posts', to: 'posts#index', as: :result_posts
: 診断結果のページにアクセスするルート(GET /posts
)です。create
アクションからこのindex
アクションにリダイレクトされます
【def create
が必要な理由を簡単に】
まず、def new
と def index
の役割について
-
new
メソッド:新しい診断フォームを表示するページを担当します。このページでユーザーは診断に必要な情報を入力します。new
は単にフォームを表示するだけで、データを保存したり処理をしたりはしません。 -
index
メソッド:診断結果を一覧で表示するページを担当します。このメソッドではすでに保存されたデータ(診断結果など)を取得して、ユーザーに見せます。
次に、create
メソッドがなぜ必要か?
-
create
メソッド:診断フォーム(new
メソッドで表示されるもの)にユーザーが入力したデータを受け取り、サーバーにそのデータを送信して保存する役割を持っています。ユーザーがフォームに入力し「送信」ボタンを押すと、POSTリクエストがサーバーに送られます。このリクエストはcreate
メソッドによって処理され、データベースに新しい診断結果が保存されます。
簡単にまとめると:
-
new
はフォームを表示するだけ(データの保存はしない)。 -
create
はフォームのデータを保存する(データが保存されないと結果を表示できない)。 -
index
は保存されたデータを表示するため、create
でデータが保存されていないと意味がない。
つまり、new
でフォームを作り、create
でそのフォームからデータを受け取って保存し、その後index
で保存された結果を表示する、という流れになります。
2つ目のエラー: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
このエラーは、JavaScript が非同期的に応答を期待しているのに、予期せぬ理由で通信が中断された場合に発生します。通常、Rails の Turbo や Ajax 機能が適切に動作していないことが原因のようで、解決策としては次の3つが挙げられます
解決策:
- アプリケーションの JavaScript コードに問題がないか確認します。
- もし Turbo を使っているなら、非同期リクエストに対する正しい応答を処理するようにしてください。
- Turbo を無効にして、通常のフォーム送信を試みることもできます。例えば、フォームタグを次のように変更します。
拡張機能によるものという記事も見られましたがとりあえずはturboに関する部分を見てみました
👇Turbo を無効にして、通常のフォーム送信するようにしてみる
変更前
# app/views/posts/new.html.erb
<%= form_tag(posts_path, method: :post) do %>
変更後
<%= form_tag(posts_path, method: :post, data: { turbo: false }) do %>
さいごにまとめ
一応上記の通りに修正することでエラーが解消されたので一安心。
ただ2つ目のエラーに関してはまだ時間がたつといつの間にか発生している状態です。
拡張機能で何か原因があるのかも
今回の記事が何か参考になれば幸いです。