【Rails】redirect_to と render の違いについて調べてみた。

Rails コントローラーの create アクションと update アクションで並んでいる redirect と render ですが、どう違うのかいまいち分からなかったので調べました。学び始めたばかりの頃はあまり気にしていませんでしたが、先日２つの違いを質問されてうまく答えられなかったので。。。

####結論

【render】
controller から指定された view を直接呼び出す。
動き: controller　→　view
データなどの状態はそのままで画面だけ貼り替える

【redirect_to】
http リクエストを受けた場合と同じ処理
動き: controller　→　URL　→　route　→　controller　→　view
指定したURLに一からアクセスしてデータをすべて取得し直す

####実際にやってみた
①通常の controller ファイルの create アクションで動きを見てみる

```tasks_controller.rb
 def create
   @task = Task.new(task_params)
   if @task.save
     redirect_to tasks_path, notice: "タスクを作成しました"
   else
     render :new
   end
 end 
```
![スクリーンショット 2020-12-28 21.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/31768a99-1b16-20a6-e2e3-524da7a04c46.png)
バリデーションに引っかかって保存されないようにタスク名は空欄。これで"Create Task" を押すと、、、
![スクリーンショット 2020-12-28 21.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/31768a99-1b16-20a6-e2e3-524da7a04c46.png)
保存されず、"render :new"が実行されました。入力したデータはそのままです。

② render を redirect に置き換えてみた。Prefixで render の場合と同じく new.html.erb を呼び出すように指定します。

```tasks_controller.rb
def create
  @task = Task.new(task_params)
  if @task.save
    redirect_to tasks_path, notice: "タスクを作成しました"
  else
    redirect_to new_task_path #render を redirect_to に置き換えた
  end
end
```

![スクリーンショット 2020-12-28 21.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/31768a99-1b16-20a6-e2e3-524da7a04c46.png)
今回も、わざとタスク名を空欄で保存。"Create Task" を押すと、、、
![スクリーンショット 2020-12-28 20.21.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/60f84f26-f7f4-7c0d-3938-995ea5bbaa33.png)
入力していた分が全て空欄になってしまいました。今回はテストで文字数が少ないから問題ありませんが、文字数の多いブログを投稿しようとしてエラーが起きた場合、書いた文章が水の泡ということになってしまいます。

何が起きているのか、困った時の Rails ガイドです。 

####Railsガイドの説明

render と redirect_to について、 Railsガイドの説明は下記。

[Railsガイド レイアウトとレンダリング 2.3 redirect_toを使用する](
https://railsguides.jp/layouts_and_rendering.html#redirect-to%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)
>renderはレスポンス構成時にどのビュー (または他のアセット) を使用するかを指定するためのものです。redirect_toメソッドは、この点においてrenderメソッドと根本的に異なります。redirect_toメソッドは、別のURLに対して改めてリクエストを再送信するよう、ブラウザに指令を出すためのものです。

[Railsガイド レイアウトとレンダリング 2.3.2 renderとredirect_toの違い](
https://railsguides.jp/layouts_and_rendering.html#render%E3%81%A8redirect-to%E3%81%AE%E9%81%95%E3%81%84)
>redirect_toを実行した後、コードはそこで実行を終了し、ブラウザからの次のリクエストを待ちます (通常のスタンバイ状態)。その直後、redirect_toでブラウザに送信したHTTPステータスコード302に従って、ブラウザから別のURLへのリクエストがサーバーに送信され、サーバーはそのリクエストを改めて処理します。それ以外のことは行っていません。

ちょっと分かりにくいですが。。。

もう一つこちらの記事も大変参考にさせていただき、何が起きているか分かってきました。
[render と redirect の違い](https://qiita.com/january108/items/54143581ab1f03deefa1)

render は入力した内容を保持しつつ、指定された view を直に呼び出して画面を張り替える。redirect_to は http リクエストを改めて要求して、route や controller を通って指定された view を一から呼び出し直しているということのようです。

ちなみに、もう一つ試してみました。

③ redirect_to を render に変えてみる
タスクの保存が成功したら index アクションに移動する設定にしてみました。 

```tasks_controller.rb
def create
  @task = Task.new(task_params)
  if @task.save
    render :index
  else
    render :new
  end
end
```
今度はタスクが保存されるように、タスク名も含めて全ての項目を入力。
![スクリーンショット 2020-12-28 20.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/7c42a201-d620-8404-48ad-924461a19b9c.png)

これで Create Taskを押してみると。。。
![スクリーンショット 2020-12-28 15.45.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/f055a67a-1b7b-112a-9126-dceca9f957c1.png)
エラーが発生しました。これは index.html.erb の インスタンス変数 @task に何も入っていないためと思われます。@task は controller の index アクションで生成されます。つまり指定された view を呼び出そうとしているものの、controller の index アクションを経由していないのでインスタンス変数が作成されていない。そのためエラーが発生していると思われます。

一方でredirect_toを使ってみると。

```tasks_controller.rb
def create
  @task = Task.new(task_params)
  if @task.save
    redirect_to tasks_path
  else
    render :new
  end
end
```
保存されるように、全ての項目を入力。
![スクリーンショット 2020-12-28 20.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/7c42a201-d620-8404-48ad-924461a19b9c.png)
Create Task を押すと。。
![スクリーンショット 2020-12-28 23.14.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/a9f9fbbf-92b9-9511-19d7-6c3e17382da7.png)
タスクが保存され、 index の view に移動してくれました。