Rails と Rack

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

このガイドの内容:

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

1 Rack入門

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

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

2 RailsとRack

2.1 RackアプリケーションとしてのRailsアプリケーション

Rails.applicationはRailsアプリケーションをRackアプリケーションとして実装したものです。Rackに準拠したWebサーバーで、Railsアプリケーションを提供するには、Rails.applicationオブジェクトを使用する必要があります。

2.2 rails serverコマンド

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

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

また次のようにして、ミドルウェアを読み込みます。

def middleware
  middlewares = []
  middlewares << [Rails::Rack::Debugger] if options[:debugger]
  middlewares << [::Rack::ContentLength]
  Hash.new(middlewares)
end

Rails::Rack::Debuggerは主としてdevelopment環境で役に立ちます。読み込まれたミドルウェアの役割は下表のとおりです。

ミドルウェア 役割
Rails::Rack::Debugger デバッガを起動する
Rack::ContentLength レスポンスのバイト数を計算し、HTTP Content-Length ヘッダーをセットする

2.3 rackupコマンド

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

# Rails.root/config.ru
require ::File.expand_path('../config/environment', __FILE__)

use Rails::Rack::Debugger
use Rack::ContentLength
run Rails.application

サーバーを起動します。

$ rackup config.ru

rackupのオプションについて詳しく知りたいときは下記のようにします。

$ rackup --help

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

Action Dispatcher内部のコンポーネントの多くは、Rackのミドルウェアとして実装されています。Rails内外の様々なミドルウェアを結合して、完全なRailsのRackアプリケーションを作るために、Rails::ApplicationActionDispatch::MiddlewareStackを使用しています。

ActionDispatch::MiddlewareStackRack::BuilderのRails版ですが、Railsアプリケーションの要求を満たすために、より柔軟性があり、多機能なクラスになっています。

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

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

$ bin/rails middleware

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

use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000029a0838>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run Rails.application.routes

デフォルトのミドルウェア(とその他のうちいくつか)についてはミドルウェアスタックの内容 を参照してください。

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

ミドルウェアスタックにミドルウェアを追加したり、削除したり、変更したりするにはapplication.rbもしくは環境ごとのenvironments/<environment>.rbファイル内でconfig.middlewareをいじります。

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

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

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

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

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

# config/application.rb

# Rack::BounceFaviconを一番最後に追加する
config.middleware.use Rack::BounceFavicon

# ActiveRecord::QueryCacheの後にLifo::Cacheを追加する
# またLifo::Cacheに{ page_cache: false }を渡す
config.middleware.insert_after ActiveRecord::QueryCache, 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::Lock"

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

$ bin/rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8>
use Rack::Runtime
...
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.serve_static_assetsfalseにするとオフになります。

Rack::Lock

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

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

  • メモリによるキャッシュを行うために使用します。このキャッシュはスレッドセーフではありません。

Rack::Runtime

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

Rack::MethodOverride

  • params[:_method]が存在するときに、(HTTPの)メソッドを上書きます。HTTPのPUTメソッド、DELETEメソッドを実現するためのミドルウェアです。

ActionDispatch::RequestId

  • ユニークなidを生成してX-Request-Idヘッダーに設定します。ActionDispatch::Request#uuidメソッドも同一のidを利用しています。

Rails::Rack::Logger

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

ActionDispatch::ShowExceptions

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

ActionDispatch::DebugExceptions

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

ActionDispatch::RemoteIp

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

ActionDispatch::Reloader

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

ActionDispatch::Callbacks

  • リクエストの処理を開始する前に、prepareコールバックを起動します(訳注: この説明は原文レベルで間違っており、現在原文の修正を行っています)。

ActiveRecord::Migration::CheckPending

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

ActiveRecord::ConnectionAdapters::ConnectionManagement

  • リクエストを処理する度にデータベースへのコネクションをコネクションプールに返します。env['rack.test']trueでない場合のみ返却が行われます。

ActiveRecord::QueryCache

  • Active Recordのクエリキャッシュを有効にします。

ActionDispatch::Cookies

  • クッキー機能を提供します。

ActionDispatch::Session::CookieStore

  • クッキーにセッションを保存するようにします。

ActionDispatch::Flash

  • flash機能を提供します(flashとは連続するリクエスト間で値を共有する機能です)。これは、config.action_controller.session_storeに値が設定されている場合にのみ有効です。

ActionDispatch::ParamsParser

  • リクエストからパラメータをパースして、paramsを設定します。

ActionDispatch::Head

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

Rack::ConditionalGet

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

Rack::ETag

  • bodyが文字列のみのレスポンスに対して、ETagヘッダを追加します。 ETagはキャッシュの有効性を検証するのに使用されます。

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

4 参考資料

4.1 Rackについて詳しく学ぶ

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