edge
詳細はrubyonrails.orgで: もっとRuby on Rails

Action Controllerの概要

このガイドでは、コントローラがどのように機能し、アプリケーションのリクエストサイクルにどのように組み込まれるかを学びます。

このガイドを読み終えると、以下のことがわかるようになります。

1 コントローラの役割は何ですか?

Action Controllerは、MVCのCです。ルータがリクエストに使用するコントローラを決定した後、コントローラはリクエストを理解し、適切な出力を生成する責任を持ちます。幸いなことに、Action Controllerはほとんどの基本的な作業を代わりに行い、スマートな規約を使用してこれをできるだけ簡単にします。

ほとんどの一般的なRESTfulアプリケーションでは、コントローラはリクエストを受け取り(これは開発者としては見えません)、モデルからデータを取得または保存し、ビューを使用してHTMLの出力を作成します。コントローラが少し異なる方法で作業する必要がある場合でも、問題ありません。これはコントローラの最も一般的な動作方法です。

したがって、コントローラはモデルとビューの間の仲介役と考えることができます。コントローラはモデルデータをビューで利用できるようにし、ユーザデータをモデルに保存または更新します。

注意:ルーティングプロセスの詳細については、Rails Routing from the Outside Inを参照してください。

2 コントローラの命名規則

Railsのコントローラの命名規則は、コントローラ名の最後の単語を複数形にすることを好みますが、厳密に必要ではありません(例:ApplicationController)。例えば、ClientsControllerClientControllerよりも好ましいですし、SiteAdminsControllerSiteAdminControllerSitesAdminsControllerよりも好ましいです。

この規則に従うことで、デフォルトのルートジェネレータ(resourcesなど)を使用する際に、各:path:controllerを修飾する必要がなくなり、名前付きルートヘルパーの使用方法をアプリケーション全体で一貫させることができます。詳細については、Layouts and Rendering Guideを参照してください。

注意:コントローラの命名規則は、モデルの命名規則とは異なります。モデルは単数形で命名することが期待されています。

3 メソッドとアクション

コントローラは、ApplicationControllerを継承し、他のクラスと同様にメソッドを持つRubyクラスです。アプリケーションがリクエストを受け取ると、ルーティングはどのコントローラとアクションを実行するかを決定し、Railsはそのコントローラのインスタンスを作成し、アクションと同じ名前のメソッドを実行します。

class ClientsController < ApplicationController
  def new
  end
end

例えば、ユーザが新しいクライアントを追加するためにアプリケーションの/clients/newにアクセスした場合、RailsはClientsControllerのインスタンスを作成し、そのnewメソッドを呼び出します。上記の例の空のメソッドは問題ありません。なぜなら、Railsはデフォルトでnew.html.erbビューをレンダリングするからです。newメソッドでは、新しいClientを作成することで、@clientインスタンス変数をビューで利用できるようにすることができます。

def new
  @client = Client.new
end

詳細については、Layouts and Rendering Guideを参照してください。

ApplicationControllerActionController::Baseを継承しており、多くの便利なメソッドが定義されています。このガイドではいくつかをカバーしますが、気になる方はAPIドキュメントやソース自体で全てを確認することができます。

アクションとして呼び出せるのは公開メソッドのみです。補助メソッドやフィルタなど、アクションでないメソッドの可視性を下げる(privateprotectedを使用する)ことはベストプラクティスです。

警告:一部のメソッド名はAction Controllerによって予約されています。これらをアクションや補助メソッドとして意図せず再定義すると、SystemStackErrorが発生する可能性があります。コントローラをRESTfulな[Resource Routing][]アクションに制限する場合は、これについて心配する必要はありません。

注意:予約されたメソッドをアクション名として使用する必要がある場合、予約されたメソッド名を非予約のアクションメソッドにマップするためにカスタムルートを使用するという回避策があります。

4 パラメータ

おそらく、コントローラのアクションでユーザーから送信されたデータや他のパラメータにアクセスしたいと思うでしょう。Webアプリケーションでは、2種類のパラメータが可能です。最初はURLの一部として送信されるパラメータで、クエリ文字列パラメータと呼ばれます。クエリ文字列はURLの"?"以降の部分です。2番目のタイプのパラメータは通常POSTデータと呼ばれます。この情報は通常、ユーザーが入力したHTMLフォームから取得されます。POSTデータはHTTP POSTリクエストの一部としてのみ送信できるため、POSTデータと呼ばれます。Railsはクエリ文字列パラメータとPOSTパラメータの区別を行わず、両方がコントローラのparamsハッシュで利用できます。

class ClientsController < ApplicationController
  # このアクションはHTTP GETリクエストによって実行されるため、クエリ文字列パラメータが使用されますが、これはパラメータへのアクセス方法には影響しません。このアクションのURLは、アクティブ化されたクライアントをリストするために次のようになります: /clients?status=activated
  def index
    if params[:status] == "activated"
      @clients = Client.activated
    else
      @clients = Client.inactivated
    end
  end

  # このアクションはPOSTパラメータを使用します。これらはおそらくユーザーが送信したHTMLフォームから来るものです。このRESTfulリクエストのURLは"/clients"になり、データはリクエストボディの一部として送信されます。
  def create
    @client = Client.new(params[:client])
    if @client.save
      redirect_to @client
    else
      # この行はデフォルトのレンダリング動作を上書きします。デフォルトでは、"create"ビューがレンダリングされます。
      render "new"
    end
  end
end

4.1 ハッシュと配列のパラメータ

paramsハッシュは1次元のキーと値に限定されません。ネストされた配列やハッシュを含めることができます。値の配列を送信するには、キー名の末尾に空の角括弧 "[]" を追加します:

GET /clients?ids[]=1&ids[]=2&ids[]=3

注: この例の実際のURLは、"["と"]"の文字はURLでは許可されていないため、"/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3"とエンコードされます。ほとんどの場合、ブラウザが自動的にエンコードしてくれるため、心配する必要はありませんが、サーバーに手動でそのようなリクエストを送信する必要がある場合は、これを念頭に置いてください。

params[:ids]の値は、["1", "2", "3"]になります。パラメータの値は常に文字列です。Railsは型を推測したりキャストしたりしません。

注: params内の[nil][nil, nil, ...]などの値は、セキュリティ上の理由からデフォルトでは[]に置き換えられます。詳細については、セキュリティガイドを参照してください。

ハッシュを送信するには、角括弧の内部にキー名を含めます:

<form accept-charset="UTF-8" action="/clients" method="post">
  <input type="text" name="client[name]" value="Acme" />
  <input type="text" name="client[phone]" value="12345" />
  <input type="text" name="client[address][postcode]" value="12345" />
  <input type="text" name="client[address][city]" value="Carrot City" />
</form>

このフォームが送信されると、params[:client]の値は{ "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }になります。params[:client][:address]にはネストされたハッシュが含まれていることに注意してください。

paramsオブジェクトはハッシュのように振る舞いますが、キーとしてシンボルと文字列を相互に使用することができます。

4.2 JSONパラメータ

アプリケーションがAPIを公開している場合、JSON形式のパラメータを受け入れることが多いでしょう。リクエストの「Content-Type」ヘッダが「application/json」に設定されている場合、Railsは自動的にパラメータをparamsハッシュにロードします。通常通りにアクセスすることができます。

例えば、次のJSONコンテンツを送信している場合:

{ "company": { "name": "acme", "address": "123 Carrot Street" } }

コントローラはparams[:company]として{ "name" => "acme", "address" => "123 Carrot Street" }を受け取ります。

また、初期化子でconfig.wrap_parametersをオンにしたり、コントローラでwrap_parametersを呼び出したりしている場合、JSONパラメータでルート要素を省略することができます。この場合、パラメータはクローンされ、コントローラの名前に基づいてキーが選択されてラップされます。したがって、上記のJSONリクエストは次のように書くことができます:

{ "name": "acme", "address": "123 Carrot Street" }

そして、データをCompaniesControllerに送信している場合、それは次のように:companyキーでラップされます: ruby { name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } }

APIドキュメントを参照して、キーの名前やラップする特定のパラメータをカスタマイズすることができます。

注意:XMLパラメータの解析のサポートは、actionpack-xml_parserというジェムに抽出されました。

4.3 ルーティングパラメータ

paramsハッシュには常に:controller:actionのキーが含まれますが、これらの値にアクセスするためにはcontroller_nameメソッドとaction_nameメソッドを使用する必要があります。:idなどのルーティングで定義された他のパラメータも利用可能です。例えば、アクティブなクライアントまたは非アクティブなクライアントを表示するリストを考えてみましょう。"pretty"なURLで:statusパラメータをキャプチャするルートを追加することができます。

get '/clients/:status', to: 'clients#index', foo: 'bar'

この場合、ユーザーがURL /clients/active を開いた場合、params[:status]は "active" に設定されます。このルートが使用されると、params[:foo]もクエリ文字列で渡されたかのように "bar" に設定されます。コントローラーはまた、params[:action]を "index" として、params[:controller]を "clients" として受け取ります。

4.4 default_url_options

コントローラー内でdefault_url_optionsというメソッドを定義することで、URL生成のためのグローバルなデフォルトパラメータを設定することができます。このメソッドは、シンボルでキーが指定されたハッシュを返す必要があります。

class ApplicationController < ActionController::Base
  def default_url_options
    { locale: I18n.locale }
  end
end

これらのオプションは、URLを生成する際の出発点として使用されるため、url_forの呼び出しに渡されたオプションによって上書きされる可能性があります。

上記の例のように、ApplicationControllerdefault_url_optionsを定義すると、これらのデフォルトはすべてのURL生成に使用されます。このメソッドは特定のコントローラーで定義することもできますが、その場合は生成されたURLにのみ影響を与えます。

リクエスト内では、このメソッドは実際にはすべての生成されたURLに対して呼び出されるわけではありません。パフォーマンスのために、返されたハッシュはキャッシュされ、リクエストごとに最大1回の呼び出しがあります。

4.5 ストロングパラメータ

ストロングパラメータを使用すると、Active ModelのマスアサインメントでAction Controllerのパラメータを許可する前に、許可する必要があります。これにより、マスアップデートのために許可する属性を意識的に選択する必要があります。これは、ユーザーが誤って機密性の高いモデル属性を更新することを防ぐためのより良いセキュリティの実践です。

さらに、パラメータを必須としてマークすることができ、定義済みのraise/rescueフローを通じて流れることになります。必須のパラメータがすべて渡されていない場合、400 Bad Requestが返されます。

class PeopleController < ActionController::Base
  # これは明示的な許可のステップなしにマスアサインメントを使用しているため、ActiveModel::ForbiddenAttributesError例外が発生します。
  def create
    Person.create(params[:person])
  end

  # これは、パラメータにpersonキーがある限り、問題なくパスします。それ以外の場合は、ActionController::ParameterMissing例外が発生し、ActionController::Baseによってキャッチされて400 Bad Requestエラーに変換されます。
  def update
    person = current_account.people.find(params[:id])
    person.update!(person_params)
    redirect_to person
  end

  private
    # 許可されたパラメータをカプセル化するためにプライベートメソッドを使用するのは良いパターンです。これにより、createとupdateの間で同じ許可リストを再利用できます。また、このメソッドを特定のユーザーの許可属性のチェックに特化させることもできます。
    def person_params
      params.require(:person).permit(:name, :age)
    end
end

4.5.1 許可されたスカラー値

permitを以下のように呼び出すと:

params.permit(:id)

指定されたキー(:id)がparamsに現れ、許可されたスカラー値が関連付けられている場合、そのキーは含まれるように許可されます。それ以外の場合、キーはフィルタリングされますので、配列、ハッシュ、または他のオブジェクトは注入されません。

許可されたスカラータイプは、StringSymbolNilClassNumericTrueClassFalseClassDateTimeDateTimeStringIOIOActionDispatch::Http::UploadedFile、およびRack::Test::UploadedFileです。

paramsの値が許可されたスカラー値の配列であることを宣言するには、キーを空の配列にマップします。

params.permit(id: [])

ハッシュパラメータまたはその内部構造の有効なキーを宣言することができない場合や便利ではない場合があります。その場合は、空のハッシュにマップします。

params.permit(preferences: {})

ただし、これにより任意の入力が可能になるため、注意が必要です。この場合、permitは返された構造内の値が許可されたスカラーであることを保証し、それ以外のものはフィルタリングされます。 permit!メソッドを使用すると、パラメータのハッシュ全体を許可することができます。

params.require(:log_entry).permit!

これにより、:log_entryパラメータのハッシュとそのサブハッシュが許可され、許可されたスカラーのチェックは行われません。つまり、どんな値でも受け入れられます。permit!を使用する際には非常に注意が必要です。なぜなら、現在のおよび将来のモデル属性のすべてが一括代入されるようになるからです。

4.5.2 ネストされたパラメータ

次のように、ネストされたパラメータにもpermitを使用することができます。

params.permit(:name, { emails: [] },
              friends: [ :name,
                         { family: [ :name ], hobbies: [] }])

この宣言では、nameemailsfriends属性が許可されます。emailsは許可されたスカラー値の配列であることが期待され、friendsは特定の属性を持つリソースの配列であることが期待されます。friendsname属性(許可されたスカラー値が許可される)と、hobbies属性(許可されたスカラー値の配列)を持ち、family属性はnameを持つことが制限されています(ここでも許可されたスカラー値が許可されます)。

4.5.3 その他の例

newアクションでも許可された属性を使用することができます。ただし、通常、newを呼び出すときにはルートキーをrequireで使用できないため、問題が発生します。

# `fetch`を使用するとデフォルト値を指定し、
# そこからStrong Parameters APIを使用できます。
params.fetch(:blog, {}).permit(:title, :author)

モデルクラスメソッドaccepts_nested_attributes_forを使用すると、関連するレコードを更新および削除することができます。これはid_destroyパラメータに基づいています。

# :idと:_destroyを許可する
params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy])

整数キーを持つハッシュは異なる方法で処理され、直接の子として属性を宣言することができます。これは、accepts_nested_attributes_forhas_many関連と組み合わせて使用した場合に得られるパラメータです。

# 次のデータを許可するために:
# {"book" => {"title" => "Some Book",
#             "chapters_attributes" => { "1" => {"title" => "First Chapter"},
#                                        "2" => {"title" => "Second Chapter"}}}}

params.require(:book).permit(:title, chapters_attributes: [:title])

製品名を表すパラメータと、その製品に関連付けられた任意のデータのハッシュを許可したい場合を想像してみてください。製品名属性とデータハッシュ全体を許可する必要があります。

def product_params
  params.require(:product).permit(:name, data: {})
end

4.5.4 Strong Parametersの範囲外

Strong Parameters APIは、最も一般的な使用例を考慮して設計されています。すべてのパラメータフィルタリングの問題を解決するための銀の弾丸としては意図されていません。ただし、APIを独自のコードと簡単に組み合わせて、状況に適応させることができます。

5 セッション

アプリケーションには、各ユーザーごとにセッションがあり、リクエスト間で永続化される少量のデータを格納することができます。セッションはコントローラとビューでのみ利用可能であり、いくつかの異なるストレージメカニズムのいずれかを使用できます。

すべてのセッションストアは、セッションごとに一意のIDを保存するためにクッキーを使用します(セッションIDをURLに渡すことはセキュリティ上の理由から許可されません)。

ほとんどのストアでは、このIDを使用してサーバー上のセッションデータを検索します(たとえば、データベーステーブル内)。ただし、CookieStoreというデフォルトで推奨されるセッションストアには例外があります。CookieStoreは、すべてのセッションデータをクッキー自体に保存します(IDは必要に応じて使用できます)。これにより、非常に軽量になり、新しいアプリケーションでセッションを使用するためのセットアップが不要になります。クッキーデータは暗号化されており、改ざん防止のために暗号署名されています。また、アクセス権を持つ人物が内容を読むことはできないようにもなっています(編集された場合、Railsは受け入れません)。

CookieStoreは約4 kBのデータを保存できますが、他のストアよりもはるかに少ないです。どのセッションストアを使用しても、大量のデータをセッションに保存することはお勧めしません。特に、モデルインスタンスなどの複雑なオブジェクトをセッションに保存することは避けるべきです。なぜなら、サーバーがリクエスト間でそれらを再構築できない場合にエラーが発生する可能性があるからです。 もしユーザーセッションが重要なデータを保存しない場合や長期間必要ない場合(例えばメッセージングのためにフラッシュを使用する場合)は、ActionDispatch::Session::CacheStoreを使用することを検討できます。これにより、セッションはアプリケーションで設定したキャッシュの実装を使用して保存されます。これにより、追加のセットアップや管理は必要ありませんが、既存のキャッシュインフラストラクチャを使用してセッションを保存できます。もちろん、セッションは一時的であり、いつでも消える可能性があるというデメリットがあります。

セッションストレージについては、セキュリティガイドを参照してください。

異なるセッションストレージメカニズムが必要な場合は、イニシャライザで変更することができます。

Rails.application.config.session_store :cache_store

詳細については、設定ガイドのconfig.session_storeを参照してください。

Railsはセッションデータに署名する際にセッションキー(クッキーの名前)を設定します。これもイニシャライザで変更することができます。

# このファイルを変更した場合は、サーバーを再起動してください。
Rails.application.config.session_store :cookie_store, key: '_your_app_session'

domainキーを渡すことで、クッキーのドメイン名を指定することもできます。

# このファイルを変更した場合は、サーバーを再起動してください。
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"

Railsは(CookieStoreの場合)config/credentials.yml.encでセッションデータの署名に使用するシークレットキーを設定します。bin/rails credentials:editを使用して変更することができます。

# aws:
#   access_key_id: 123
#   secret_access_key: 345

# RailsのすべてのMessageVerifierで使用されるベースシークレット。
secret_key_base: 492f...

注意:CookieStoreを使用している場合、secret_key_baseを変更するとすべての既存のセッションが無効になります。

5.1 セッションへのアクセス

コントローラーでは、sessionインスタンスメソッドを介してセッションにアクセスできます。

注意:セッションは遅延ロードされます。アクションのコードでセッションにアクセスしない場合、セッションはロードされません。したがって、セッションを無効にする必要はありません。アクセスしないだけで十分です。

セッションの値はハッシュのようなキー/値のペアとして保存されます。

class ApplicationController < ActionController::Base
  private
    # :current_user_idというキーでセッションに保存されたIDを持つユーザーを検索します。
    # これはRailsアプリケーションでユーザーログインを処理する一般的な方法です。
    # ログイン時にセッション値を設定し、ログアウト時に削除します。
    def current_user
      @_current_user ||= session[:current_user_id] &&
        User.find_by(id: session[:current_user_id])
    end
end

セッションに何かを保存するには、ハッシュのようにキーに割り当てます。

class LoginsController < ApplicationController
  # ログインを作成します(ユーザーをログインさせます)
  def create
    if user = User.authenticate(params[:username], params[:password])
      # ユーザーIDをセッションに保存して、後続のリクエストで使用できるようにします
      session[:current_user_id] = user.id
      redirect_to root_url
    end
  end
end

セッションから何かを削除するには、キー/値のペアを削除します。

class LoginsController < ApplicationController
  # ログインを削除します(ユーザーをログアウトさせます)
  def destroy
    # セッションからユーザーIDを削除します
    session.delete(:current_user_id)
    # メモ化された現在のユーザーをクリアします
    @_current_user = nil
    redirect_to root_url, status: :see_other
  end
end

セッション全体をリセットするには、reset_sessionを使用します。

5.2 フラッシュ

フラッシュはセッションの特別な部分であり、各リクエストでクリアされます。これは、次のリクエストでのみ使用できるため、エラーメッセージなどを渡すのに便利です。

フラッシュはflashメソッドを介してアクセスされます。セッションと同様に、フラッシュもハッシュとして表されます。

ログアウトのアクションを例にしてみましょう。コントローラーは次のリクエストでユーザーに表示されるメッセージを送信できます。

class LoginsController < ApplicationController
  def destroy
    session.delete(:current_user_id)
    flash[:notice] = "ログアウトに成功しました。"
    redirect_to root_url, status: :see_other
  end
end

リダイレクトの一部としてフラッシュメッセージを割り当てることも可能です。:notice:alert、または汎用の:flashを割り当てることができます。

redirect_to root_url, notice: "ログアウトに成功しました。"
redirect_to root_url, alert: "ここから抜け出せません!"
redirect_to root_url, flash: { referral_code: 1234 }

destroyアクションはアプリケーションのroot_urlにリダイレクトし、メッセージが表示されます。前のアクションがフラッシュに入れたエラーアラートや通知を表示するかどうかは、次のアクション次第です。フラッシュからのエラーアラートや通知をアプリケーションのレイアウトで表示するのが一般的です。 ```erb <!-- --> <% flash.each do |name, msg| -%> <%= content_tag :div, msg, class: name %> <% end -%>

<!-- more content -->

```

この方法では、アクションが通知またはアラートメッセージを設定した場合、レイアウトは自動的に表示されます。

セッションが保存できるものなら何でも渡すことができます。通知やアラートに限定されません。

<% if flash[:just_signed_up] %>
  <p class="welcome">Welcome to our site!</p>
<% end %>

フラッシュの値を別のリクエストに引き継ぐ場合は、flash.keepを使用します。

class MainController < ApplicationController
  # このアクションはroot_urlに対応するものですが、ここにリダイレクトされるすべてのリクエストをUsersController#indexにリダイレクトしたい場合があります。
  # アクションがフラッシュを設定してここにリダイレクトすると、別のリダイレクトが発生すると値は通常失われますが、'keep'を使用して別のリクエストで永続化することができます。
  def index
    # すべてのフラッシュの値を永続化します。
    flash.keep

    # 特定の種類の値のみを保持するためにキーを使用することもできます。
    # flash.keep(:notice)
    redirect_to users_url
  end
end

5.2.1 flash.now

デフォルトでは、フラッシュに値を追加すると、次のリクエストでそれらの値にアクセスできるようになりますが、同じリクエストでそれらの値にアクセスしたい場合もあります。たとえば、createアクションがリソースの保存に失敗し、newテンプレートを直接レンダリングする場合、新しいリクエストは発生しませんが、フラッシュを使用してメッセージを表示したい場合があります。これを行うには、通常のflashと同じようにflash.nowを使用できます。

class ClientsController < ApplicationController
  def create
    @client = Client.new(client_params)
    if @client.save
      # ...
    else
      flash.now[:error] = "Could not save client"
      render action: "new"
    end
  end
end

6 Cookies

アプリケーションは、クライアントに小さなデータを保存することができます。これはクッキーと呼ばれ、リクエストやセッションを超えて永続化されます。Railsはcookiesメソッドを介してクッキーへの簡単なアクセスを提供します。これはsessionと同様にハッシュのように動作します。

class CommentsController < ApplicationController
  def new
    # クッキーにコメント者の名前が保存されている場合は、自動的にコメント者の名前を入力します。
    @comment = Comment.new(author: cookies[:commenter_name])
  end

  def create
    @comment = Comment.new(comment_params)
    if @comment.save
      flash[:notice] = "Thanks for your comment!"
      if params[:remember_name]
        # コメント者の名前を記憶します。
        cookies[:commenter_name] = @comment.author
      else
        # コメント者の名前のクッキーを削除します。
        cookies.delete(:commenter_name)
      end
      redirect_to @comment.article
    else
      render action: "new"
    end
  end
end

セッションの値に対してキーをnilに設定することで値を削除できますが、クッキーの値を削除する場合はcookies.delete(:key)を使用する必要があります。

Railsは、署名付きクッキージャーと暗号化されたクッキージャーも提供しており、機密データを保存するために使用できます。署名付きクッキージャーは、クッキー値に暗号署名を追加してその完全性を保護します。暗号化されたクッキージャーは、署名に加えて値を暗号化するため、エンドユーザーによって読み取ることができません。詳細については、APIドキュメントを参照してください。

これらの特別なクッキージャーは、シリアライザを使用して割り当てられた値を文字列にシリアライズし、読み取り時にRubyオブジェクトに逆シリアル化します。config.action_dispatch.cookies_serializerを使用して使用するシリアライザを指定できます。

新しいアプリケーションのデフォルトシリアライザは:jsonです。JSONはRubyオブジェクトのラウンドトリップに対して制限があることに注意してください。たとえば、DateTime、およびSymbolオブジェクト(Hashキーを含む)は、Stringにシリアライズおよび逆シリアル化されます。

class CookiesController < ApplicationController
  def set_cookie
    cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
    redirect_to action: 'read_cookie'
  end

  def read_cookie
    cookies.encrypted[:expiration_date] # => "2014-03-20"
  end
end

これらやより複雑なオブジェクトを保存する必要がある場合は、後続のリクエストでそれらの値を読み取るときに値を手動で変換する必要がある場合があります。

クッキーセッションストアを使用する場合、上記はsessionflashハッシュにも適用されます。

7 レンダリング

ActionControllerを使用すると、HTML、XML、またはJSONデータのレンダリングが容易になります。スキャフォールディングを使用してコントローラを生成した場合、次のようになります。

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render xml: @users }
      format.json { render json: @users }
    end
  end
end

上記のコードでは、render xml: @usersではなくrender xml: @users.to_xmlを使用していることに注意してください。オブジェクトが文字列でない場合、Railsは自動的にto_xmlを呼び出します。 レンダリングについては、レイアウトとレンダリングガイドで詳細を学ぶことができます。

8 フィルター

フィルターは、コントローラーアクションの「前」「後」「周囲」で実行されるメソッドです。

フィルターは継承されるため、ApplicationControllerでフィルターを設定すると、アプリケーション内のすべてのコントローラーで実行されます。

「前」フィルターは、before_actionを介して登録されます。リクエストサイクルを停止することがあります。一般的な「前」フィルターは、アクションの実行にユーザーのログインが必要なものです。フィルターメソッドは次のように定義できます。

class ApplicationController < ActionController::Base
  before_action :require_login

  private
    def require_login
      unless logged_in?
        flash[:error] = "このセクションにアクセスするにはログインする必要があります"
        redirect_to new_login_url # リクエストサイクルを停止
      end
    end
end

このメソッドは、ユーザーがログインしていない場合にフラッシュにエラーメッセージを保存し、ログインフォームにリダイレクトします。もし「前」フィルターがレンダリングまたはリダイレクトを行った場合、アクションは実行されません。そのフィルターの後に実行される予定の追加のフィルターもキャンセルされます。

この例では、フィルターがApplicationControllerに追加され、したがってアプリケーション内のすべてのコントローラーがそれを継承します。これにより、アプリケーション内のすべてのものが使用するためにユーザーのログインが必要になります。明らかな理由から(最初にログインできないため!)、すべてのコントローラーやアクションがこれを必要とするわけではありません。skip_before_actionを使用して、特定のアクションの前でこのフィルターが実行されないようにすることができます。

class LoginsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]
end

これにより、LoginsControllernewcreateアクションは、ユーザーがログインしている必要がないままで動作します。onlyオプションは、これらのアクションのみでこのフィルターをスキップするために使用され、逆の方法で機能するexceptオプションもあります。これらのオプションは、フィルターを追加する際にも使用できるため、最初から選択したアクションのみで実行されるフィルターを追加することができます。

注意:同じフィルターを異なるオプションで複数回呼び出すことはできません。最後のフィルター定義が前の定義を上書きします。

8.1 「後」フィルターと「周囲」フィルター

「前」フィルターに加えて、アクションが実行された後にフィルターを実行したり、アクションの前後の両方でフィルターを実行したりすることもできます。

「後」フィルターは、after_actionを介して登録されます。これらは「前」フィルターと似ていますが、アクションが既に実行されているため、クライアントに送信されるレスポンスデータにアクセスできます。明らかに、「後」フィルターはアクションの実行を停止することはできません。なお、「後」フィルターは、リクエストサイクルで例外が発生した場合を除いて、成功したアクションの後にのみ実行されます。

「周囲」フィルターは、around_actionを介して登録されます。関連するアクションを実行するために、yieldを使用します。これはRackミドルウェアの動作と似ています。

たとえば、変更に承認ワークフローがあるウェブサイトでは、管理者はトランザクション内で変更を適用することで簡単にプレビューすることができます。

class ChangesController < ApplicationController
  around_action :wrap_in_transaction, only: :show

  private
    def wrap_in_transaction
      ActiveRecord::Base.transaction do
        begin
          yield
        ensure
          raise ActiveRecord::Rollback
        end
      end
    end
end

「周囲」フィルターはレンダリングもラップします。特に上記の例では、ビュー自体がデータベースから読み取る場合(スコープを介してなど)、トランザクション内で読み取りが行われ、プレビュー用のデータが表示されます。

yieldせずにレスポンスを自分で構築することも選択できます。その場合、アクションは実行されません。

8.2 フィルターの他の使用方法

フィルターを使用する最も一般的な方法は、プライベートメソッドを作成し、before_actionafter_action、またはaround_actionを使用してそれらを追加することですが、同じことを行うための他の2つの方法もあります。

最初の方法は、*_actionメソッドと直接ブロックを使用する方法です。ブロックはコントローラーを引数として受け取ります。上記のrequire_loginフィルターは、ブロックを使用して次のように書き直すことができます。

class ApplicationController < ActionController::Base
  before_action do |controller|
    unless controller.send(:logged_in?)
      flash[:error] = "このセクションにアクセスするにはログインする必要があります"
      redirect_to new_login_url
    end
  end
end

この場合、フィルターはsendを使用しています。なぜなら、logged_in?メソッドはプライベートであり、フィルターはコントローラーのスコープで実行されないからです。これは、この特定のフィルターを実装するための推奨される方法ではありませんが、より単純な場合には役立つ場合があります。 特にaround_actionについて、ブロックはactionでも呼び出されます。

around_action { |_controller, action| time(&action) }

2番目の方法は、フィルタリングを処理するためにクラス(実際には、適切なメソッドに応答する任意のオブジェクト)を使用することです。これは、より複雑で、他の2つの方法では読みやすく再利用可能な方法で実装できない場合に便利です。例えば、ログインフィルタを再度クラスを使用して書き直すことができます。

class ApplicationController < ActionController::Base
  before_action LoginFilter
end

class LoginFilter
  def self.before(controller)
    unless controller.send(:logged_in?)
      controller.flash[:error] = "You must be logged in to access this section"
      controller.redirect_to controller.new_login_url
    end
  end
end

再度、このフィルタには理想的な例ではありません。なぜなら、コントローラのスコープではなく、引数としてコントローラが渡されるからです。フィルタクラスは、フィルタと同じ名前のメソッドを実装する必要があります。したがって、before_actionフィルタの場合、クラスはbeforeメソッドを実装する必要があります。aroundメソッドはアクションを実行するためにyieldする必要があります。

9 リクエスト偽装防止

クロスサイトリクエストフォージェリ(CSRF)は、サイトがユーザーを騙して、ユーザーの知識や許可なしに他のサイトにリクエストを送信し、データを追加、変更、削除する攻撃の一種です。

これを回避するための最初のステップは、すべての「破壊的な」アクション(作成、更新、削除)にはGETリクエスト以外でのアクセスしかできないようにすることです。RESTfulな規約に従っている場合、すでにこれを行っています。ただし、悪意のあるサイトは簡単に非GETリクエストをあなたのサイトに送信することができます。そのため、リクエスト偽装防止が必要です。その名前が示すように、偽装されたリクエストから保護します。

これは、サーバーだけが知っている予測不可能なトークンを各リクエストに追加することで行われます。これにより、適切なトークンのないリクエストが来た場合、アクセスが拒否されます。

次のようなフォームを生成する場合:

<%= form_with model: @user do |form| %>
  <%= form.text_field :username %>
  <%= form.text_field :password %>
<% end %>

トークンが隠しフィールドとして追加されるのがわかります:

<form accept-charset="UTF-8" action="/users/1" method="post">
<input type="hidden"
       value="67250ab105eb5ad10851c00a5621854a23af5489"
       name="authenticity_token"/>
<!-- fields -->
</form>

Railsは、フォームヘルパーを使用して生成されたすべてのフォームにこのトークンを追加しますので、ほとんどの場合は心配する必要はありません。フォームを手動で作成するか、他の理由でトークンを追加する必要がある場合は、form_authenticity_tokenメソッドを使用して取得できます。

form_authenticity_tokenは有効な認証トークンを生成します。これは、Railsが自動的に追加しない場所(カスタムAjax呼び出しなど)で便利です。

セキュリティガイドには、これについての詳細な説明と、Webアプリケーションを開発する際に意識すべき他のセキュリティ関連の問題があります。

10 リクエストとレスポンスオブジェクト

すべてのコントローラには、現在実行中のリクエストサイクルに関連付けられたリクエストおよびレスポンスオブジェクトを指す2つのアクセサメソッドがあります。requestメソッドにはActionDispatch::Requestのインスタンスが含まれ、responseメソッドはクライアントに送信されるレスポンスオブジェクトを返します。

10.1 requestオブジェクト

リクエストオブジェクトには、クライアントからのリクエストに関する多くの有用な情報が含まれています。利用可能なメソッドの完全なリストについては、Rails APIドキュメントRackドキュメントを参照してください。このオブジェクトでアクセスできるプロパティの一部は次のとおりです。

requestのプロパティ 目的
host このリクエストに使用されたホスト名。
domain(n=2) ホスト名の右側(TLD)から始まる最初のnセグメントのホスト名。
format クライアントが要求したコンテンツタイプ。
method リクエストに使用されたHTTPメソッド。
get?, post?, patch?, put?, delete?, head? HTTPメソッドがGET/POST/PATCH/PUT/DELETE/HEADの場合はtrueを返します。
headers リクエストに関連するヘッダーを含むハッシュを返します。
port リクエストに使用されるポート番号(整数)。
protocol 使用されるプロトコルと「://」を含む文字列を返します。 例:「http://」
query_string URLのクエリ文字列部分、「?」以降のすべて。
remote_ip クライアントのIPアドレス。
url リクエストに使用される完全なURL。

10.1.1 path_parametersquery_parameters、および request_parameters

Railsは、クエリ文字列やPOSTボディの一部として送信されたすべてのパラメータを、paramsハッシュに収集します。リクエストオブジェクトには、これらのパラメータにアクセスするための3つのアクセサがあります。query_parametersハッシュには、クエリ文字列の一部として送信されたパラメータが含まれています。request_parametersハッシュには、POSTボディの一部として送信されたパラメータが含まれています。path_parametersハッシュには、この特定のコントローラとアクションに至るパスの一部としてルーティングによって認識されたパラメータが含まれています。

10.2 responseオブジェクト

通常、レスポンスオブジェクトは直接使用されることはありませんが、アクションの実行とデータのレンダリング中に構築され、ユーザーに送信されるデータにアクセスするために直接レスポンスにアクセスすることがあります。これらのアクセサメソッドの一部には、値を変更することができるセッターもあります。使用可能なメソッドの完全なリストについては、Rails APIドキュメントRackドキュメントを参照してください。

responseのプロパティ 目的
body クライアントに送信されるデータの文字列です。通常はHTMLです。
status レスポンスのHTTPステータスコード。例えば、成功したリクエストの場合は200、ファイルが見つからない場合は404です。
location クライアントがリダイレクトされるURL(ある場合)。
content_type レスポンスのコンテンツタイプ。
charset レスポンスに使用される文字セット。デフォルトは "utf-8" です。
headers レスポンスに使用されるヘッダー。

10.2.1 カスタムヘッダーの設定

レスポンスにカスタムヘッダーを設定する場合は、response.headersを使用します。headers属性は、ヘッダー名を値にマッピングするハッシュであり、Railsはいくつかのヘッダーを自動的に設定します。ヘッダーを追加または変更する場合は、次のようにresponse.headersに代入するだけです。

response.headers["Content-Type"] = "application/pdf"

注意:上記の場合、content_typeセッターを直接使用する方が意味があります。

11 HTTP認証

Railsには、3つの組み込みのHTTP認証メカニズムがあります。

  • ベーシック認証
  • ダイジェスト認証
  • トークン認証

11.1 ベーシック認証

ベーシック認証は、ほとんどのブラウザや他のHTTPクライアントでサポートされている認証スキームです。例として、ユーザー名とパスワードをブラウザのHTTPベーシックダイアログウィンドウに入力することでのみ利用可能な管理セクションを考えてみましょう。組み込みの認証を使用するには、http_basic_authenticate_withメソッドを使用するだけです。

class AdminsController < ApplicationController
  http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end

これを設定すると、AdminsControllerから継承する名前空間のコントローラを作成できます。このフィルタは、これらのコントローラのすべてのアクションで実行され、HTTPベーシック認証で保護されます。

11.2 ダイジェスト認証

ダイジェスト認証は、基本認証よりも優れた認証方法です。なぜなら、クライアントがネットワーク上で暗号化されていないパスワードを送信する必要がないからです(ただし、HTTPベーシック認証はHTTPS上では安全です)。Railsでダイジェスト認証を使用するには、authenticate_or_request_with_http_digestメソッドを使用するだけです。

class AdminsController < ApplicationController
  USERS = { "lifo" => "world" }

  before_action :authenticate

  private
    def authenticate
      authenticate_or_request_with_http_digest do |username|
        USERS[username]
      end
    end
end

上記の例のように、authenticate_or_request_with_http_digestブロックは1つの引数(ユーザー名)を取ります。ブロックはパスワードを返します。authenticate_or_request_with_http_digestからfalseまたはnilを返すと、認証に失敗します。

11.3 トークン認証

トークン認証は、HTTP AuthorizationヘッダーでBearerトークンを使用するためのスキームです。利用可能なトークン形式は多数あり、それらの詳細についてはこのドキュメントの範囲外です。

例えば、事前に発行された認証トークンを使用して認証とアクセスを行いたい場合を考えてみましょう。Railsでトークン認証を実装するには、authenticate_or_request_with_http_tokenメソッドを使用するだけです。

class PostsController < ApplicationController
  TOKEN = "secret"

  before_action :authenticate

  private
    def authenticate
      authenticate_or_request_with_http_token do |token, options|
        ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
      end
    end
end

上記の例のように、authenticate_or_request_with_http_tokenブロックは2つの引数(トークンとHTTP Authorizationヘッダーから解析されたオプションを含むHash)を取ります。ブロックは認証が成功した場合にtrueを返す必要があります。authenticate_or_request_with_http_tokenからfalseまたはnilを返すと、認証に失敗します。

12 ストリーミングとファイルのダウンロード

HTMLページをレンダリングする代わりに、ファイルをユーザーに送信したい場合があります。Railsのすべてのコントローラーには、send_datasend_fileメソッドがあり、どちらもデータをクライアントにストリーミングします。send_fileは、ディスク上のファイルの名前を指定し、そのファイルの内容をストリーミングする便利なメソッドです。

クライアントにデータをストリーミングするには、send_dataを使用します:

require "prawn"
class ClientsController < ApplicationController
  # クライアントの情報を含むPDFドキュメントを生成し、返します。ユーザーはPDFをファイルとしてダウンロードします。
  def download_pdf
    client = Client.find(params[:id])
    send_data generate_pdf(client),
              filename: "#{client.name}.pdf",
              type: "application/pdf"
  end

  private
    def generate_pdf(client)
      Prawn::Document.new do
        text client.name, align: :center
        text "住所:#{client.address}"
        text "メール:#{client.email}"
      end.render
    end
end

上記の例のdownload_pdfアクションは、実際にPDFドキュメントを生成して文字列として返すプライベートメソッドを呼び出します。この文字列は、ファイルとしてダウンロードされるためにクライアントにストリーミングされ、ユーザーにファイル名が提案されます。ファイルをユーザーにストリーミングする場合、ファイルをダウンロードさせたくない場合があります。例えば、HTMLページに埋め込むことができる画像です。ファイルがダウンロードされないようにブラウザに伝えるには、:dispositionオプションを「inline」に設定します。このオプションの逆でデフォルトの値は「attachment」です。

12.1 ファイルの送信

ディスク上に既に存在するファイルを送信する場合は、send_fileメソッドを使用します。

class ClientsController < ApplicationController
  # 既に生成されディスク上に保存されたファイルをストリーミングします。
  def download_pdf
    client = Client.find(params[:id])
    send_file("#{Rails.root}/files/clients/#{client.id}.pdf",
              filename: "#{client.name}.pdf",
              type: "application/pdf")
  end
end

これにより、ファイルを4KBずつ読み取り、ストリーミングします。一度にファイル全体をメモリに読み込むことを避けるためです。ストリーミングをオフにするには、:streamオプションをオフにするか、ブロックサイズを:buffer_sizeオプションで調整できます。

:typeが指定されていない場合、:filenameで指定されたファイル拡張子から推測されます。拡張子に対してコンテンツタイプが登録されていない場合は、application/octet-streamが使用されます。

警告:クライアントからのデータ(params、cookiesなど)を使用してディスク上のファイルを検索する場合は注意してください。これはセキュリティリスクであり、意図しないファイルにアクセスできる可能性があります。

TIP:可能であれば、静的ファイルをRailsを介してストリーミングするのではなく、Webサーバーのパブリックフォルダに保持することをお勧めします。ユーザーにApacheや他のWebサーバーを使用してファイルを直接ダウンロードさせることで、リクエストがRailsのスタック全体を通過するのを不必要に防ぐことができます。

12.2 RESTfulなダウンロード

send_dataは問題ありませんが、RESTfulなアプリケーションを作成している場合、ファイルのダウンロードのために別々のアクションを作成する必要はありません。RESTの用語では、上記の例のPDFファイルはクライアントリソースの別の表現と見なすことができます。Railsには「RESTful」なダウンロードを行うためのスマートな方法が用意されています。以下は、ダウンロードがストリーミングなしでshowアクションの一部としてPDFダウンロードに書き換える方法です:

class ClientsController < ApplicationController
  # ユーザーはこのリソースをHTMLまたはPDFとして受け取ることを要求できます。
  def show
    @client = Client.find(params[:id])

    respond_to do |format|
      format.html
      format.pdf { render pdf: generate_pdf(@client) }
    end
  end
end

この例が動作するためには、RailsにPDFのMIMEタイプを追加する必要があります。これは、config/initializers/mime_types.rbファイルに次の行を追加することで行うことができます:

Mime::Type.register "application/pdf", :pdf

注意:設定ファイルは各リクエストでリロードされませんので、変更が反映されるようにサーバーを再起動する必要があります。

これで、ユーザーはURLに「.pdf」を追加するだけで、クライアントのPDFバージョンをリクエストできます:

GET /clients/1.pdf

12.3 任意のデータのライブストリーミング

Railsでは、ファイルだけでなく、他の任意のデータもストリーミングすることができます。実際には、レスポンスオブジェクトで任意のデータをストリーミングすることができます。ActionController::Liveモジュールを使用すると、ブラウザとの持続的な接続を作成できます。このモジュールを使用すると、特定のタイミングでブラウザに任意のデータを送信することができます。

12.3.1 ライブストリーミングの組み込み

コントローラクラス内にActionController::Liveを含めると、コントローラ内のすべてのアクションでデータのストリーミングが可能になります。以下のようにモジュールをミックスインすることができます。

class MyController < ActionController::Base
  include ActionController::Live

  def stream
    response.headers['Content-Type'] = 'text/event-stream'
    100.times {
      response.stream.write "hello world\n"
      sleep 1
    }
  ensure
    response.stream.close
  end
end

上記のコードは、ブラウザとの持続的な接続を維持し、1秒ごとに"hello world\n"というメッセージを100回送信します。

上記の例にはいくつかの注意点があります。レスポンスストリームを閉じることを忘れないようにする必要があります。ストリームを閉じずに放置すると、ソケットが永久に開いたままになります。また、レスポンスストリームに書き込む前にコンテンツタイプをtext/event-streamに設定する必要があります。これは、レスポンスがコミットされた後(response.committed?が真の値を返すとき)にヘッダを書き込むことができないためです。レスポンスストリームをwriteまたはcommitすると、コミットが行われます。

12.3.2 使用例

カラオケマシンを作成しているとしましょう。ユーザーが特定の曲の歌詞を取得したい場合、各Songには特定の行数があり、各行は歌い終わるのにnum_beatsの時間がかかります。

もしも歌詞をカラオケ形式で返す(前の行が終わった後に次の行を送信する)場合、ActionController::Liveを以下のように使用できます。

class LyricsController < ActionController::Base
  include ActionController::Live

  def show
    response.headers['Content-Type'] = 'text/event-stream'
    song = Song.find(params[:id])

    song.each do |line|
      response.stream.write line.lyrics
      sleep line.num_beats
    end
  ensure
    response.stream.close
  end
end

上記のコードは、前の行が終わった後に次の行を送信します。

12.3.3 ストリーミングの考慮事項

任意のデータをストリーミングすることは非常に強力なツールです。前の例に示されているように、レスポンスストリームでいつ何を送信するかを選択することができます。ただし、以下の点にも注意する必要があります。

  • 各レスポンスストリームは新しいスレッドを作成し、元のスレッドからスレッドローカル変数をコピーします。スレッドローカル変数が多すぎるとパフォーマンスに悪影響を与える可能性があります。同様に、大量のスレッドもパフォーマンスを低下させる可能性があります。
  • レスポンスストリームを閉じるのを忘れると、対応するソケットが永久に開いたままになります。レスポンスストリームを使用する場合は、必ずcloseを呼び出してください。
  • WEBrickサーバーはすべてのレスポンスをバッファリングするため、ActionController::Liveを含めることはできません。自動的にレスポンスをバッファリングしないWebサーバーを使用する必要があります。

13 ログのフィルタリング

Railsは、logフォルダ内の各環境ごとにログファイルを保持しています。これらはアプリケーションの実際の動作をデバッグする際に非常に便利ですが、ライブアプリケーションではすべての情報をログファイルに保存したくない場合があります。

13.1 パラメータのフィルタリング

アプリケーションの設定でconfig.filter_parametersにフィルタリングしたい機密情報のリクエストパラメータを追加することで、ログファイルから機密情報をフィルタリングすることができます。これらのパラメータはログ内で[FILTERED]とマークされます。

config.filter_parameters << :password

注意:指定されたパラメータは部分一致の正規表現でフィルタリングされます。Railsは、passwordpassword_confirmationmy_tokenなどの一般的なアプリケーションパラメータを処理するために、適切なイニシャライザ(initializers/filter_parameter_logging.rb)にデフォルトのフィルタリスト(:passw:secret:tokenなど)を追加します。

13.2 リダイレクトのフィルタリング

アプリケーションがリダイレクトしている機密の場所をログファイルからフィルタリングすることが望ましい場合があります。config.filter_redirect構成オプションを使用することで、それが可能です。

config.filter_redirect << 's3.amazonaws.com'

これは、文字列、正規表現、またはその両方の配列に設定することができます。

config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]

一致するURLは'[FILTERED]'とマークされます。

14 Rescue

おそらく、アプリケーションにはバグが含まれているか、それ以外の例外が発生し、それを処理する必要があります。たとえば、ユーザーがデータベースに存在しないリソースへのリンクをたどる場合、Active RecordはActiveRecord::RecordNotFound例外をスローします。

Railsのデフォルトの例外処理では、すべての例外に対して「500 Server Error」というメッセージが表示されます。リクエストがローカルで行われた場合、トレースバックと追加情報が表示されるため、何が間違っているのかを特定して対処することができます。リクエストがリモートである場合、Railsはユーザーに対して単純な「500 Server Error」というメッセージ、またはルーティングエラーがあった場合は「404 Not Found」というメッセージ、またはレコードが見つからなかった場合は「500 Server Error」というメッセージを表示します。これらのエラーがどのようにキャッチされ、ユーザーに表示されるかをカスタマイズしたい場合、Railsアプリケーションではいくつかのレベルの例外処理が利用可能です。

14.1 デフォルトの500と404のテンプレート

デフォルトでは、本番環境ではアプリケーションは404エラーメッセージまたは500エラーメッセージをレンダリングします。開発環境では、すべての未処理の例外が単に発生します。これらのメッセージは、publicフォルダ内の静的なHTMLファイルである404.htmlおよび500.htmlに含まれています。これらのファイルをカスタマイズして、追加の情報やスタイルを追加することができますが、これらは静的なHTMLです。つまり、ERB、SCSS、CoffeeScript、またはレイアウトを使用することはできません。

14.2 rescue_from

エラーをキャッチする際にもう少し複雑な処理を行いたい場合は、rescue_fromを使用できます。これは、特定のタイプ(または複数のタイプ)の例外をコントローラ全体およびそのサブクラスで処理します。

rescue_fromディレクティブによってキャッチされる例外が発生すると、例外オブジェクトがハンドラに渡されます。ハンドラは、メソッドまたは:withオプションに渡されるProcオブジェクトです。明示的なProcオブジェクトの代わりに、ブロックを直接使用することもできます。

以下は、rescue_fromを使用してすべてのActiveRecord::RecordNotFoundエラーをキャッチし、それらに対して何かしらの処理を行う方法です。

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

  private
    def record_not_found
      render plain: "404 Not Found", status: 404
    end
end

もちろん、この例は複雑さを増し、デフォルトの例外処理を改善するわけではありませんが、これらの例外をキャッチできるようになると、自由に処理を行うことができます。たとえば、アプリケーションの特定のセクションにアクセス権限がない場合にスローされるカスタム例外クラスを作成することができます。

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, with: :user_not_authorized

  private
    def user_not_authorized
      flash[:error] = "このセクションにアクセスする権限がありません。"
      redirect_back(fallback_location: root_path)
    end
end

class ClientsController < ApplicationController
  # ユーザーがクライアントにアクセスするための適切な権限を持っているかを確認します。
  before_action :check_authorization

  # アクションはすべての認証について心配する必要がないことに注意してください。
  def edit
    @client = Client.find(params[:id])
  end

  private
    # ユーザーが認可されていない場合は、例外をスローします。
    def check_authorization
      raise User::NotAuthorized unless current_user.admin?
    end
end

警告:ExceptionまたはStandardErrorrescue_fromを使用すると、Railsが例外を適切に処理できなくなり、重大な副作用が発生する可能性があります。そのため、強い理由がない限り、これを行うことはお勧めしません。

注意:本番環境で実行する場合、すべてのActiveRecord::RecordNotFoundエラーは404エラーページをレンダリングします。カスタムの動作が必要でない限り、これを処理する必要はありません。

注意:特定の例外は、コントローラが初期化される前に発生し、アクションが実行されるため、ApplicationControllerクラスからのみ救済できます。

15 HTTPSプロトコルの強制

コントローラへの通信がHTTPSでのみ可能であることを確認したい場合は、config.force_sslを使用してActionDispatch::SSLミドルウェアを有効にすることで実現できます。

16 組み込みのヘルスチェックエンドポイント

Railsには、/upパスでアクセス可能な組み込みのヘルスチェックエンドポイントも用意されています。このエンドポイントは、アプリケーションが例外なしで起動した場合には200のステータスコードを返し、それ以外の場合には500のステータスコードを返します。

本番環境では、多くのアプリケーションはステータスを上流に報告する必要があります。これは、障害が発生した場合にエンジニアに通知するアップタイムモニター、ポッドのヘルスを判断するために使用されるロードバランサーまたはKubernetesコントローラなどです。このヘルスチェックは、多くの状況で動作するワンサイズフィットオールの設計です。

新しく生成されたRailsアプリケーションでは、ヘルスチェックは/upにありますが、"config/routes.rb"でパスを任意のものに設定することができます。

Rails.application.routes.draw do
  get "healthz" => "rails/health#show", as: :rails_health_check
end

ヘルスチェックは、/healthzパスを介してアクセスできるようになります。

注意:このエンドポイントは、データベースやRedisクラスタなど、アプリケーションのすべての依存関係の状態を反映していません。アプリケーション固有のニーズがある場合は、"rails/health#show"を独自のコントローラアクションに置き換えてください。

チェックする内容については慎重に考えてください。サードパーティのサービスが悪化したためにアプリケーションが再起動される状況になる可能性があります。理想的には、アプリケーションを障害が発生しても正常に処理できるように設計する必要があります。

フィードバック

このガイドの品質向上にご協力ください。

タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。

不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。

修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください

そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。