Railsでcreate後にrenderするとパスが変わるのは何故か
どうも、てーやまです。
『ユーザ登録に失敗した場合は、前画面に戻って入力をやり直させたい』
これ、よくある話だと思います。
その場合、『render』ではなく『redirect_to』を使うのが正解だと思われますが、これは何故かについて理由を書いていきます。
サンプルプログラム
勉強のために作っているアプリ『Mochibe(モチベ)』をサンプルにして、起こったエラーとその対処を書いていきます。
ユーザ登録画面

コントローラ
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
flash[:info] = "#{@user.name}さんをシステムに新規登録しました"
redirect_to studies_path
else
flash[:danger] = @user.errors.full_messages
render "new" ←①
end
usersコントローラでは、ビューから渡ってきたストロングパラメータの内容をバリデーションチェックし、usersテーブルを追加します。
この時、バリデーションエラーとなると、フラッシュにエラーメッセージを設定して、前画面に戻り、再度入力を促そうとします。…①の部分
renderすると何が問題なのか
この場合、以下の画像のようにビューが呼び出されますが、パスが変わっていることに気がつくと思います。

これは何故かというと、『renderはどのビューを返却するかを指定するだけであり、HTTPリクエストが再送されないから』です。
要は、『HTTPリクエストが送信されているのはcreateアクション』なので、Railsは『createアクションのルーティング(/users(.:format))のまま、/users/new.html.erbを返却している』のです。
Routing Errorが発生する可能性がある
パスが変わったとしても、表示されているビューは意図したものであるから良いと考えるかもしれません。
しかし、ユーザの意図しない操作によりエラーが発生する可能性があります。
例えば以下のような場合です。
- ページを意図的にリロードした(F5キー等)
- スマホでセッションが切れた状態で再接続し、意図せずリロードされた
この場合はどうなるかというと、ルーティングの設定によるので一概には言えませんが、以下のいずれかになる可能性が高いです。
- users/indexアクションが実行される
- Routing Errorとなる
僕の場合は、『Routing Error』となりました。

理由は、変更されたパス『/users(.:format)に対してHTTPリクエストが行われたため』です。
この場合Railsは、users_pathに対応するアクションを呼び出そうとするので、resources
でルーティングしている場合には「users#index」、「/users/index.html.erb」を呼び出そうとし、そうでなければGETに対応するアクション、ビューを呼び出そうとします。
僕の場合は、それらが無かったためエラーとなりました。
どう対処するのか
renderではなく、redirect_toを使います。
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
flash[:info] = "#{@user.name}さんをシステムに新規登録しました"
redirect_to studies_path
else
flash[:danger] = @user.errors.full_messages
redirect_to new_user_path
end
end
こうすることで、HTTPリクエストを再送し、『/users/new.html.erb』を呼び出すことができます(正規のルートで前画面に戻れる)
これだとリロードされても問題ありません。
バリデーションエラーメッセージを引き継ぐためには、flashを使えば良いです。
ディスカッション
コメント一覧
まだ、コメントがありません