【Rails】renderの解説!基本・引数・オプションを完全理解!

renderメソッドまとめRuby on Rails

render は Rails で HTTP レスポンスを作るための大事なメソッドです。
初心者の方は特に render メソッドをなんとなく使っていないでしょうか?
かくいう私も Rails 学びたてのころは既存のコードを参考に勘で使っていました。

勘でなんとかなるのは最初のうちだけで、すぐに限界が訪れます。
なにより、よく分からないメソッドを使うのは不安が残ります。
Rails のコードを自信を持って書けるようになったのは render メソッドの仕組みやルールをしっかりと把握してからでした。

本記事では render メソッドの基本から引数・オプションの仕組みについて徹底的に解説します。

render メソッドを理解できれば Rails 力がみるみる向上しますよ!

render メソッドとは

renderの仕組み

render メソッドは以下のいずれかを指定してHTTPレスポンスを作成します
(上図ではビューテンプレートのファイルパスを渡してHTTPレスポンスを作成する様子を模式化しています)

  • ビューテンプレートのファイルパス
  • オブジェクト
  • 文字列

render メソッドはコントローラとビューから呼び出せます。

コントローラから呼び出す render

まずコントローラから呼び出す render について解説します。
基本形は以下の通りです。

render 'ビューテンプレートのファイルパス'

ビューテンプレートのファイルパスを指定します。

ファイルパスには省略ルールがあります。
例えば以下のときは app/views/xxx/yyy.html.erb が利用されるビューテンプレートとなります。

render 'xxx/yyy'

ただし、さらに省略できるルールがあります。
詳しいルールについては以下の記事を参照してください。

>> 【Rails】render メソッドの引数のルールを徹底解説!!【初心者向け】

ビューから呼び出す render (部分テンプレート, パーシャルテンプレート)

ビューで使用する render は、ビューの中で部分テンプレートを差し込むためのメソッドです。
基本形は以下の通りです。

<%= render '部分テンプレートのファイルパス', 変数名: 値, ・・・ %>

ファイルパスのルールはコントローラのrenderと同じですが、ファイル名の先頭にアンダースコア(_)をつけるというルールが追加されます。
例えば以下であれば app/views/xxx/_yyy.html.erb となります。

<%= render 'xxx/yyy' %>

第2引数は部分テンプレートに渡す値を変数名をつけて指定します。
例えば

<%= render 'xxx/yyy', x: 1, y: 2 %>

とした場合、app/views/xxx/_yyy.html.erb 側に変数 x, y で値を受け渡し、

<p><%= x %></p>
<p><%= y %></p>

のようなコードを書いた場合、実行すると以下のように変換されます。

<p>1</p>
<p>2</p>

HTML以外の形式で返す(JSON, XML, Plain text)

render メソッドでは HTML 以外の形式でもレスポンスとして返せます。
代表的な形式として以下の形式を紹介します。

  • JSON
  • XML
  • プレーンテキスト

JSON

JSON形式で返すためには render メソッドの引数に json オプションを指定し、以下の形式で記述します。

render json: オブジェクト

オブジェクトには Hash やモデルのインスタンスなど様々な値を指定できます。
例えば以下は詳細ページを表示する変わりに1件のモデルのインスタンスを JSON 形式で返しています。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render json: @post # ← 取得したモデルのインスタンスを指定
  end
end

ブラウザでアクセスすると JSON 形式のレスポンスを確認できます。

XML

XML 形式で返すには render メソッドの引数に xml オプションを指定し、以下の形式で記述します。

render xml: オブジェクト

特に Hash を渡せば対応した XML の形式に整形して返してくれます。
例えば以下のように取得したモデルのインスタンスを attributes メソッドでHash に変換して xml: オプションに渡すことができます。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render xml: @post.attributes # ← 取得したモデルのインスタンスを Hash に変換(attributes)して指定
  end
end

ブラウザでアクセスすると XML 形式のレスポンスを確認できます。

プレーンテキスト

HTML や JSON など構造化されたテキストではなく、普通のテキスト(プレーンテキスト)として返すこともできます。
プレーンテキストで返す場合は以下の形式で記述します。

render plain: 文字列

例えば以下のようにフォーマットしたテキストを plain: オプションに渡すとプレーンテキストで返します。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render plain: "ID: #{@post.id}\nタイトル: #{@post.title}" # ← 文字列にフォーマットした
  end
end

ブラウザでアクセスすると以下のように表示されます。

ステータスコードを指定する

renderで作成されるHTTPレスポンスのステータスコードはデフォルトでは 200 (ok) です。
「失敗」を表したい場合は別のステータスコードを設定したい場合があります。
render では status: オプションでステータスコードを指定できます。

render 'xxx/yyy', status: 番号もしくはシンボル

例えば 400 (Bad Request) を指定するには以下のように記述します。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render :show, status: 400
  end
end

ステータスコードは Google Chrome であればデベロッパーツールのネットワークタブで確認できます。

ステータスコードをデベロッパーツールで確認する

ステータスコード毎にシンボルが用意されています。
400の場合は :bad_request となり、以下のように記述できます。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    render :show, status: :bad_request
  end
end

各ステータスコードに対するシンボルは以下の通りです。

ステータスコードシンボル
100:continue
101:switching_protocols
102:processing
200:ok
201:created
202:accepted
203:non_authoritative_information
204:no_content
205:reset_content
206:partial_content
207:multi_status
208:already_reported
226:im_used
300:multiple_choices
301:moved_permanently
302:found
303:see_other
304:not_modified
305:use_proxy
306:reserved
307:temporary_redirect
308:permanent_redirect
400:bad_request
401:unauthorized
402:payment_required
403:forbidden
404:not_found
405:method_not_allowed
406:not_acceptable
407:proxy_authentication_required
408:request_timeout
409:conflict
410:gone
411:length_required
412:precondition_failed
413:request_entity_too_large
414:request_uri_too_long
415:unsupported_media_type
416:requested_range_not_satisfiable
417:expectation_failed
422:unprocessable_entity
423:locked
424:failed_dependency
426:upgrade_required
428:precondition_required
429:too_many_requests
431:request_header_fields_too_large
500:internal_server_error
501:not_implemented
502:bad_gateway
503:service_unavailable
504:gateway_timeout
505:http_version_not_supported
506:variant_also_negotiates
507:insufficient_storage
508:loop_detected
510:not_extended
511:network_authentication_required

redirect_to との違い

render と同時に登場するのが redirect_to です。
redirect_to メソッドもHTTPレスポンスを作るためのメソッドです。
redirect_to メソッドは 300 番台のレスポンスを作成します。

以下のような形式で記述します。

redirect_to リダイレクト先のURL

たとえば、指定した ID のレコードが存在しない場合に、一覧画面にリダイレクトするには以下のように記述します。

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def show
    @post = Post.find_by(id: params[:id])
    unless @post # 指定した ID のレコードがない場合
      redirect_to posts_url # 一覧画面にリダイレクト
    end
  end
end

デベロッパーツールでステータスコードを確認すると 302 でリダイレクトされているのが確認できます。

レスポンスヘッダの Location: でリダイレクト先のURLを確認できます。

リダイレクトの仕組みについては以下も参考にしてください。

redirect_to によるリダイレクトの方法を徹底解説【Ruby on Rails】

よくあるエラー: AbstractController::DoubleRenderError

render メソッド関連でよく発生するエラーは AbstractController::DoubleRenderError です。
一回のリクエストに対して複数回 render メソッドや redirect_to メソッドが呼び出されると発生します。

例えばよくあるパターンとしては以下のように条件分岐で異常系で特別に用意した画面を表示したい場合です。

class PostsController < ApplicationController
  def show
    @post = Post.find_by(id: params[:id])
    unless @post # 見つからなかった時は
      render 'errors/not_found' # エラー画面を表示する
    end

    render @post
  end
end

上記プログラムでは @post が nil のとき(= Post.find_by(id: params[:id]) で見つからなかったとき) に以下のようなエラーとなります。

@post が nil のとき、unless の中が実行対象となり、

render 'errors/not_found'

が呼ばれます。
そして、retern などをしていないのでそこでは終わらず、

render @post

も実行しています。
このため DoubleRenderError です。

これを避けるにはどのような場合でも呼び出される render メソッドは一回だけにする必用があります。

例えば以下の様に if-else でどちらかだけを実行するようにしたり・・・

class PostsController < ApplicationController
  def show
    @post = Post.find_by(id: params[:id])
    if @post
      render @post
    else
      render 'errors/not_found'
    end
  end
end

他にも、render の時点で return すれば片方だけが実行されます。

class PostsController < ApplicationController
  def show
    @post = Post.find_by(id: params[:id])
    unless @post
      return render 'errors/not_found' # render メソッドを呼びつつ return する
    end

    render @post
  end
end

before_action でアクションを停止する

render の効果で見過ごされがちなのが before_action で実行すると後続の処理を止めるということです。

例えば以下のように before_action で「対象のデータが見つからないときにエラー画面を表示する」ということをやってみましょう。

class PostsController < ApplicationController
  before_action :set_post

  def show
    render @post
  end

  private

  def set_post
    @post = Post.find_by(id: params[:id])
    unless @post
      render 'errors/not_found' # ここが実行されると render @post は実行されない
    end
  end
end

この場合は show メソッドが実行される前に set_post メソッドが実行されますが、@post が nil の場合、render ‘errors/not_found’ が実行されます。
render メソッドが before_action である set_post 内で実行された場合は show メソッド自体が実行されません。
このように、before_action 内で render メソッドを呼び出せば後続の処理を止められます。

before_action で render メソッドを呼び出して後続処理を止めるのは、show メソッド本体をシンプルにするためです。
before_action 側でエラー処理を実施することで、show メソッド本体から複雑な処理を省略できます。

まとめ

Rails の render メソッドについて解説しました。

render メソッドは HTTP レスポンスを作るための大切なメソッドですが初心者のうちはなんとなく使いがちです。
なんとなくで通用するのは最初のうちだけです。
この機会にしっかりと理解して Rails プログラムを自信をもって書けるようにしましょう!

タイトルとURLをコピーしました