railsチュートリアルの7章では、実際にブラウザからリクエストを送信し、ユーザー登録ができるようにします。
その際、UsersControllerのcreateメソッドは以下のようにしました。
def create
@user = User.new(user_params)
if @user.save
flash[:success] = '<メッセージ>'
redirect_to @user # user詳細にredirectする
else
render 'new', status: :unprocessable_entity
end
end
チュートリアル内ではざっと以下のように説明されています。
- redirectなどの処理がない状態でブラウザからリクエスト
- → エラーとなり、原因はcreateに対応するviewファイルがないこと
- ただ、webアプリではcreateなどの操作後は詳細ページなどにリダイレクトするのが一般的で、そちらを採用する
という感じです。
画面の動き的にもしっくりくるのであまり気にしていませんでしたが、ふとどうしてリダイレクトが一般的なのだろうと思い調べたので、記憶定着 & 備忘録としての記事になります。
なぜ、user作成などの後はredirectするのか?
結論、リロード(など)をした場合に再度postメソッドを送信してしまう心配がない ため、安全であるということかなと思います。
ここで、redirectをした場合としなかった場合のリクエストの流れが重要になります。
redirectをしない場合
例えば、createに対応するviewファイルがあったとします。その場合、以下のような順序でcreate viewが表示されます。
- クライアント(ブラウザ)から、user作成リクエスト(POST)が送信される
- UsersController#createで、user作成が成功し、create viewが返される
- クライアント側でcreateページが表示され、終了
という形です。この場合、1度のリクエストで全てが完結します。
redirectの場合
では、次にredirectをした場合をみます。
- クライアント(ブラウザ)から、user作成リクエスト(POST)が送信される
-
UsersController#create
で、user作成が成功しUsers#show
にもう一度リクエストしてね!(リダイレクトしてね!)というレスポンスが返される - クライアントは、それに従いユーザー詳細ページをリクエスト(GET)する
-
Users#show
でshow viewが返される - クライアント側でshowページが表示される
このように、ブラウザの操作では一度のみリクエストしているように見えるものの、動作的には2度のリクエストがなされ、終了します。
じゃあ、どうして安全なの?
というところですが、ブラウザでのリロードは「前回のリクエストを再送信する」という動作をします。よってredirectをしない場合、画面をリロードすると前回のリクエストである「User作成リクエスト(Post)」をもう一度実行することになります。
すると、意図せず再度リクエストをするといったことが発生しやすくなり、安全ではありません。
ブラウザで「再度送信しますか?」などのアラートがと出るのは、こういった動作で再リクエストしようとしている時、危ないから確認してね、という注意喚起ですね。
※上記の通り、表示されているurlにgetを送るといった動作でないことに注意でしょうか。
ところがredirectを利用すると、最後のリクエストがgetメソッドとなり、リロードした際意図せずデータを作成してしまう、などのいわば事故が発生しなくなります。
ですのでベーシックなwebアプリでは、基本的にDBへ変更のあるリクエストが完了した際はリダイレクトをするのが一般的、ということだと思います。
また、そのことをPRG(Post - Redirect - Get)
パターンと言ったりもするそうです。
余談
ではもし、create後に「完了ページ」のようなものを特別用意したいといった場合はどうするのだろう?と思いましたが、単純にUsersController#complete
メソッド + complete テンプレートを作成し、そこにredirectすれば良い、ということでした。
確かに。