本篇需要先了解 Rack 協定,以及 Rack 的相關概念,譬如:什麼是 Middleware、什麼是 URL 映射以及 Rack::Builder
等知識。
1 Rack 簡介
Rack 給使用 Ruby 開發的網路應用程式,提供了精簡、模組化、容易介接的介面。Rack 將 HTTP 請求與響應,盡可能包裝成最簡單的形式,給網路框架、伺服器以及框架與伺服器之間的軟體(Middleware)提供了一個統一的 API 接口,call
方法。
更多內容請參考:Rack API 文件。
深入解釋 Rack 超出本篇的範疇。若不熟悉 Rack 的基礎知識,請閱讀參考資料一節。
2 Rails on Rack
2.1 Rails 應用程式的 Rack 物件
ApplicationName::Application
是 Rails 應用程式主要的 Rack 物件。任何與 Rack 相容的 Web 伺服器,都應該使用 ApplicationName::Application
物件來執行 Rails 應用程式。Rails.application
是 ApplicationName::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
以下是如何載入 Middlewares:
def middleware middlewares = [] middlewares << [Rails::Rack::Debugger] if options[:debugger] middlewares << [::Rack::ContentLength] Hash.new(middlewares) end
Rails::Rack::Debugger
主要只在開發模式下有用。下表解釋了加載的 Middleware 的用途:
Middleware | 用途 |
---|---|
Rails::Rack::Debugger | 啟動 Debugger |
Rack::ContentLength | 計算響應有幾個 byte,並設定 HTTP Content-Length 標頭 |
2.3 rackup
若想用 rackup
來取代 rails server
,可以修改 Rails 應用程式根目錄下的 config.ru
:
# Rails.root/config.ru require ::File.expand_path('../config/environment', __FILE__) use Rack::Debugger use Rack::ContentLength run Rails.application
啟動伺服器:
$ rackup config.ru
了解 rackup
接受的其他選項:
$ rackup --help
2.4 開發自動重載
Middlewares 只會載入一次,察覺不到新的修改。有關 Middleware 的修改需要重新啟動伺服器才行。
3 Action Dispatcher Middleware Stack
許多 Action Dispatcher 的內部組件都是以 Rack Middleware 的方式所實作。Rails::Application
使用了 ActionDispatch::MiddlewareStack
,將內部與外部的 Middleware 結合起來,形成完整的 Rails Rack 應用程式。
Rails 的 ActionDispatch::MiddlewareStack
等同於 Rack::Builder
,但靈活性更高、更多功能,專門為了滿足 Rails 需求所打造。
3.1 檢視 Middleware Stack
Rails 有一個好用的 Rake 任務,可檢視使用中的 Middleware stack:
$ bin/rake 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
上列 Middlewares 在內部 Middlewares 一節分別介紹。
3.2 設定 Middleware Stack
Rails 提供了簡單的設定接口:config.middleware
,用來新增、刪除、修改 Middleware Stack 裡的 Middleware。可以在 application.rb
或是特定環境的設定檔:environments/<environment>.rb
來使用這個設定。
3.2.1 新增 Middleware
使用下面任一方法來新增 Middleware 到 Middleware Stack:
config.middleware.use(new_middleware, args)
- 新增 Middleware 到 Middleware Stack 的底部
config.middleware.insert_before(existing_middleware, new_middleware, args)
- 新增 Middleware 在某個現有的 Middleware 之前。
config.middleware.insert_after(existing_middleware, new_middleware, args)
- 新增 Middleware 在某個現有的 Middleware 之後。
# config/application.rb # Push Rack::BounceFavicon at the bottom config.middleware.use Rack::BounceFavicon # Add Lifo::Cache after ActiveRecord::QueryCache. # Pass { page_cache: false } argument to Lifo::Cache. config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, page_cache: false
3.2.2 交換 Middleware 順序
使用 config.middleware.swap
來交換現有 Middleware Stack 中,Middleware 的順序。
# config/application.rb # Replace ActionDispatch::ShowExceptions with Lifo::ShowExceptions config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions
3.2.3 刪除 Middleware
加入下行程式碼到應用程式設定檔,來刪除 Middleware:
# config/application.rb config.middleware.delete "Rack::Lock"
現在檢視 Middleware Stack,會發現 Rack::Lock
已經被刪除了。
$ bin/rake middleware use Rack::Sendfile use ActionDispatch::Static use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000029a0838> use Rack::Runtime ... run Rails.application.routes
若想移除與 Session 有關的 Middleware:
# config/application.rb config.middleware.delete "ActionDispatch::Cookies" config.middleware.delete "ActionDispatch::Session::CookieStore" config.middleware.delete "ActionDispatch::Flash"
或移除與瀏覽器相關的 Middleware:
# config/application.rb config.middleware.delete "Rack::MethodOverride"
3.3 內部 Middleware Stack
Action Controller 大多數的功能皆以 Middleware 的方式實作,以下解釋每個 Middleware 的用途:
Rack::Sendfile
- 設定伺服器的
X-Sendfile
標頭(header)。使用config.action_dispatch.x_sendfile_header
來設定。
ActionDispatch::Static
- 用來決定是否由 Rails 提供靜態 assets。使用
config.serve_static_assets
選項來啟用或禁用(true
啟用)。
Rack::Lock
- 將
env["rack.multithread"]
設為false
,則可將應用程式包在 Mutex 裡。
ActiveSupport::Cache::Strategy::LocalCache::Middleware
- 用來做 memory cache。注意,此 cache 不是線程安全的。
Rack::Runtime
- 設定 X-Runtime 標頭,並記錄請求的執行時間(秒為單位)。
Rack::MethodOverride
- 如有設定
params[:_method]
,則允許可以重寫方法。這個 Middleware 實作了 HTTPPUT
與DELETE
方法。
ActionDispatch::RequestId
- 在響應中產生獨立的
X-Request-Id
標頭,並啟用ActionDispatch::Request#uuid
方法。
Rails::Rack::Logger
- 請求開始時通知 Log,請求結束寫入 Log。
ActionDispatch::ShowExceptions
- Rescue 任何由應用程式拋出的異常,並呼叫處理異常的程式,將異常以適合的格式顯示給使用者。
ActionDispatch::DebugExceptions
- 負責記錄異常,並在請求來自本機時,顯示除錯頁面。
ActionDispatch::RemoteIp
- 檢查 IP 欺騙攻擊。
ActionDispatch::Reloader
- 準備與清除回呼。主要在開發模式下用來重新加載程式碼。
ActionDispatch::Callbacks
- 處理請求前,先執行預備好的回呼。
ActiveRecord::Migration::CheckPending
- 檢查是否有未執行的遷移檔案,有的話拋出
PendingMigrationError
錯誤。
ActiveRecord::ConnectionAdapters::ConnectionManagement
- 每個請求結束後,若
rack.test
不為真,則將作用中的連線清除。
ActiveRecord::QueryCache
- 啟用 Active Record 的查詢快取。
ActionDispatch::Cookies
- 幫請求設定 Cookie。
ActionDispatch::Session::CookieStore
- 負責把 Session 存到 Cookie。
ActionDispatch::Flash
config.action_controller.session_store
設定為真時,設定提示訊息的鍵。
ActionDispatch::ParamsParser
- 解析請求的參數放到
params
Hash 裡。
ActionDispatch::Head
- 將 HTTP
HEAD
請求轉換成GET
請求處理。
Rack::ConditionalGet
- 給伺服器加入 HTTP 的 Conditional
GET
支持,頁面沒有變化,就不會回傳響應。
Rack::ETag
- 為所有字串 Body 加上 ETag 標頭,用來驗證快取。
以上所有的 Middleware 都可以在自定的 Rack Stack 使用。
4 參考資料
4.1 學習 Rack
- Rack 官方網站
- 介紹 Rack
- Ruby on Rack #1 - Hello Rack!
- Ruby on Rack #2 - The Builder
- #317 Rack App from Scratch (pro) - RailsCasts
- #222 Rack in Rails 3 - RailsCasts
4.2 理解 Middlewares
反饋
歡迎幫忙改善指南的品質。
如發現任何錯誤之處,歡迎修正。開始貢獻前,可以先閱讀貢獻指南:文件。
翻譯如有錯誤,深感抱歉,歡迎 Fork 修正,或至此處回報。
文章可能有未完成或過時的內容。請先檢查 Edge Guides 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 Ruby on Rails 指南準則來了解行文風格。
最後,任何關於 Ruby on Rails 文件的討論,歡迎至 rubyonrails-docs 郵件論壇。