Railsでcreate後にrenderするとパスが変わるのは何故か

プログラミング

Railsでcreate後にrenderするとパスが変わるのは何故か

どうも、てーやまです。

『ユーザ登録に失敗した場合は、前画面に戻って入力をやり直させたい』
これ、よくある話だと思います。

その場合、『render』ではなく『redirect_to』を使うのが正解だと思われますが、これは何故かについて理由を書いていきます。

サンプルプログラム

勉強のために作っているアプリ『Mochibe(モチベ)』をサンプルにして、起こったエラーとその対処を書いていきます。

ユーザ登録画面

/users/new.html.erb

コントローラ

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すると何が問題なのか

この場合、以下の画像のようにビューが呼び出されますが、パスが変わっていることに気がつくと思います。

/users/new.html.erb

これは何故かというと、『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を使えば良いです。