Rails と Rack

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

このガイドの内容:

このガイドは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 rails serverコマンド

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

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 rackupコマンド

Railsのrails serverコマンドの代わりにrackupコマンドを使うときは、以下の内容をconfig.ruに記述して、Railsアプリケーションのルートディレクトリに保存します。

# Rails.root/config.ru
require_relative 'config/environment'
run Rails.application

続いて、サーバーを起動します。

$ rackup config.ru

rackupの他のオプションについて詳しく知りたいときは、以下を実行します。

$ rackup --help

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

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

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 Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
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::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/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

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

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

Rack::Sendfile

  • X-Sendfile headerを設定します。config.action_dispatch.x_sendfile_headerオプション経由で設定を変更できます。

ActionDispatch::Static

  • 静的ファイルの配信に使います。config.public_file_server.enabledfalseにするとオフになります。

Rack::Lock

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

ActionDispatch::Executor

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

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

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

ActionDispatch::RemoteIp

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

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.action_controller.session_storeに値が設定されている場合にのみ有効です。

ActionDispatch::ContentSecurityPolicy::Middleware

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

ActionDispatch::Head

  • HEADリクエストをGETに変換して処理します。その上でbodyを空にしたレスポンスを返します(訳注: Rails4.0からはRack::Headを使うように変更されています)。

Rack::Head

  • HEADリクエストをGETに変換し、GETとして処理します。

Rack::ConditionalGet

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

Rack::ETag

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

Rack::TempfileReaper

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

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

4 参考資料

4.1 Rackについて詳しく学ぶ

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