Rails と Rack

このガイドでは、RailsとRackの関係、Railsと他のRackコンポーネントとの関係について説明します。

このガイドの内容:

  • RackのミドルウェアをRailsで使う方法
  • Action Pack内のミドルウェアスタックについて
  • 独自のミドルウェアスタックを定義する方法

このガイドはRackのミドルウェア、urlマップ、Rack::BuilderといったRackのプロトコルや概念に関する実用的な知識を身につけていることを前提にしています。

1 Rack入門

Rackは、RubyのWebアプリケーションに対して、モジュール化された最小限のインターフェイスを提供して、インターフェイスを広範囲に使えるようにします。RackはHTTPリクエストとレスポンスを可能なかぎり簡単な方法でラッピングすることで、Webサーバー、Webフレームワーク、その間に位置するソフトウェア(ミドルウェアと呼ばれています)のAPIを1つのメソッド呼び出しの形にまとめます。

Rackに関する解説はこのガイドの範疇を超えてしまいます。Rackに関する基本的な知識が不足している場合は、下記のリソースを参照してください。

2 RailsとRack

2.1 RailsアプリケーションのRackオブジェクト

Rails.applicationは、Railsアプリケーションにおける主要なRackアプリケーションです。Rackに準拠したWebサーバーで、Railsアプリケーションを提供するには、Rails.applicationオブジェクトを使う必要があります。

2.2 bin/rails serverコマンド

bin/rails serverコマンドは、Rack::Serverのオブジェクトを作成し、Webサーバーを起動します。

bin/rails serverコマンドは、以下のようにRack::Serverのオブジェクトを作成します。

Rails::Server.new.tap do |server|
  require APP_PATH
  Dir.chdir(Rails.application.root)
  server.start
end

Rails::ServerクラスはRack::Serverクラスを継承しており、以下のようにRack::Server#startを呼び出します。

class Server < ::Rack::Server
  def start
    # ...
    super
  end
end

2.3 開発中の自動再読み込みについて

一度読み込まれたミドルウェアは、変更が発生しても検出されません。現在実行中のアプリケーションでミドルウェアの変更を反映するには、サーバーの再起動が必要です。

3 Action Dispatcherのミドルウェアスタック

Action Dispatcher内部のコンポーネントの多くは、「Rackミドルウェア」として実装されています。Rails::Applicationは、ActionDispatch::MiddlewareStackを用いて内部ミドルウェアや外部ミドルウェアを組み合わせることで、完全なRailsのRackアプリケーションを構築します。

RailsのActionDispatch::MiddlewareStackクラスはRack::Builderクラスと同等ですが、Railsアプリケーションの要求を満たすために柔軟性が高く多機能です。

3.1 ミドルウェアスタックを調べる

Railsには、ミドルウェアスタックを調べるための便利なタスクがあります。

$ bin/rails middleware

作成直後のRailsアプリケーションでは、以下のように出力されるはずです。

use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActionDispatch::ServerTiming
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::ActionableExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run MyApp::Application.routes

デフォルトのミドルウェアを含むいくつかのミドルウェアの概要については、ミドルウェアスタックの内部を参照してください。

3.2 ミドルウェアスタックを設定する

Railsが提供するシンプルなconfig.middlewareを用いることで、ミドルウェアスタックにミドルウェアを追加・削除・変更できます。これはapplication.rb設定ファイルで行うことも、環境ごとのenvironments/<環境名>.rb設定ファイルで行うこともできます。

3.2.1 ミドルウェアを追加する

次のメソッドを使うと、ミドルウェアスタックに新しいミドルウェアを追加できます。

  • config.middleware.use(new_middleware, args): ミドルウェアスタックの末尾に新しいミドルウェアを追加します。

  • config.middleware.insert_before(existing_middleware, new_middleware, args): 新しいミドルウェアを、(第1引数で)指定された既存のミドルウェアの直前に追加します。

  • config.middleware.insert_after(existing_middleware, new_middleware, args): 新しいミドルウェアを、(第1引数で)指定された既存のミドルウェアの直後に追加します。

# config/application.rb

# Rack::BounceFaviconを末尾に追加する
config.middleware.use Rack::BounceFavicon

# ActionDispatch::Executorの直後にLifo::Cacheを追加する
# Lifo::Cacheに引数{ page_cache: false }を渡す
config.middleware.insert_after ActionDispatch::Executor, Lifo::Cache, page_cache: false
3.2.2 ミドルウェアを置き換える

config.middleware.swapを使って、ミドルウェアスタック内にあるミドルウェアを置き換えられます。

# config/application.rb

# ActionDispatch::ShowExceptionsをLifo::ShowExceptionsで置き換える
config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions
3.2.3 ミドルウェアを移動する

ミドルウェアスタック内の既存のミドルウェアを移動して順序を変更するには、config.middleware.move_beforeconfig.middleware.move_afterを使います。

# config/application.rb

# ActionDispatch::ShowExceptionsをLifo::ShowExceptionsの前に移動
config.middleware.move_before Lifo::ShowExceptions, ActionDispatch::ShowExceptions
# config/application.rb

# ActionDispatch::ShowExceptionsをLifo::ShowExceptionsの後に移動
config.middleware.move_after Lifo::ShowExceptions, ActionDispatch::ShowExceptions
3.2.4 ミドルウェアを削除する

アプリケーションの設定に以下のコードを追加します。

# config/application.rb
config.middleware.delete Rack::Runtime

ミドルウェアスタックを調べると、Rack::Runtimeが消えていることが分かります。

$ bin/rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8>
...
run Rails.application.routes

セッション関連のミドルウェアを削除するには、次のように書きます。

# config/application.rb
config.middleware.delete ActionDispatch::Cookies
config.middleware.delete ActionDispatch::Session::CookieStore
config.middleware.delete ActionDispatch::Flash

ブラウザ関連のミドルウェアを削除するには次のように書きます。

# config/application.rb
config.middleware.delete Rack::MethodOverride

存在しないミドルウェアを削除しようとするとエラーが発生するようにするには、delete!を代わりに使います。

# config/application.rb
config.middleware.delete! ActionDispatch::Executor

3.3 ミドルウェアスタックの内部

Action Controllerの機能の多くはミドルウェアとして実装されています。それぞれの役割について以下のリストで説明します。

ActionDispatch::HostAuthorization

  • リクエストの送信先ホストを明示的に許可することで、DNSリバインディング攻撃から保護します。設定方法については、設定ガイドを参照してください。

Rack::Sendfile

ActionDispatch::Static

Rack::Lock

  • env["rack.multithread"]falseに設定し、アプリケーションをMutexでラップします。

ActionDispatch::Executor

  • スレッドセーフのコードを開発中にリロードするときに使います。

ActionDispatch::ServerTiming

  • リクエストのパフォーマンスメトリクスを含むServer-Timingヘッダーを設定します。

ActiveSupport::Cache::Strategy::LocalCache::Middleware

  • メモリキャッシュで用います。このキャッシュはスレッドセーフではありません。

Rack::Runtime

  • X-Runtimeヘッダーを設定します。このヘッダーには、リクエストの処理にかかった時間が秒単位で表示されます。

Rack::MethodOverride

  • params[:_method]が設定されている場合に(HTTP)メソッドが上書きされるようになります。HTTPのPUTメソッド、DELETEメソッドを実現するためのミドルウェアです。

ActionDispatch::RequestId

  • レスポンスでX-Request-Idヘッダーを有効にしてActionDispatch::Request#request_idメソッドが使えるようにします。

ActionDispatch::RemoteIp

  • IPスプーフィング攻撃をチェックします。

Sprockets::Rails::QuietAssets

  • アセットリクエストでのログ書き出しを抑制します。

Rails::Rack::Logger

  • リクエストの処理が開始されたことをログに出力します。リクエストが完了すると、すべてのログをフラッシュします。

ActionDispatch::ShowExceptions

  • アプリケーションが返すすべての例外をrescueし、例外処理用アプリケーション (エンドユーザー向けに例外を整形するアプリケーション) を起動します。

ActionDispatch::DebugExceptions

  • 例外をログに出力します。ローカルからのリクエストの場合は、デバッグ用ページも表示します。

ActionDispatch::Reloader

  • development環境でコードの再読み込みを行うために、prepareコールバックとcleanupコールバックを提供します。

ActionDispatch::Callbacks

  • リクエストをディスパッチする直前および直後に実行されるコールバックを提供します。

ActiveRecord::Migration::CheckPending

  • 未実行のマイグレーションがないか確認します。未実行のものがあった場合は、ActiveRecord::PendingMigrationErrorを発生させます。

ActionDispatch::Cookies

  • cookie機能を提供します。

ActionDispatch::Session::CookieStore

  • セッションをcookieに保存する役割を担当します。

ActionDispatch::Flash

  • flashのキーをセットアップします(訳注: flashは連続するリクエスト間でメッセージを共有表示する機能です)。これは、config.session_storeに値が設定されている場合にのみ有効です。

ActionDispatch::ContentSecurityPolicy::Middleware

  • Content-Security-Policyヘッダー設定用のDSLを提供します。

Rack::Head

  • すべてのHEADリクエストに対して空のbodyを返します。その他のリクエストは変更しません。

Rack::ConditionalGet

  • 「条件付きGET(Conditional GET)」機能を提供します。"条件付きGET"が有効になっていると、リクエストされたページで変更が発生していない場合に空のbodyを返します。

Rack::ETag

  • bodyが文字列のみのレスポンスにETagヘッダを追加します。ETagはキャッシュのバリデーションに使われます。

Rack::TempfileReaper

  • マルチパートリクエストをバッファするのに使われる一時ファイルをクリーンアップします。

これらのミドルウェアはいずれも、独自のRackスタックで利用することも可能です。

4 参考資料

4.1 Rackについて詳しく学ぶ

4.2 ミドルウェアを理解する

5 参考資料(日本語)

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。

Railsガイド運営チーム (@RailsGuidesJP)

支援・協賛

Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。

  1. Star
  2. このエントリーをはてなブックマークに追加