最近才看到三年前的一段影片在講 Rails 的 request 怎麼跑進 controller 的,看完覺得以前都只有模糊的印象,沒有辦法把他們完全接在一起,因此做個筆記記錄一下
Intro
我們可能都寫過這樣的 code
1 | class HelloController < ActionController::Base |
但 request 是怎麼到達 controller 裡面的? 今天就是主要解釋這部分
Into Web Server
瀏覽器端,從 cache / dns server 那邊知道 ip address 之後再送出 request,接著會抵達 web server
這些 web server 的工作是要去解析 request,知道怎麼去服務每個 request,像是 /assets/ 應該要對應到某個資料夾下面的靜態檔案,哪些網址要給他 404 頁面
這些 web server 可能是用任何語言寫的,可能是 ruby / C
至於更複雜的頁面,像是要去 database 做搜尋,要做一些動畫,這些複雜的事情就需要給更強大的 app server 處理,Rails 就是其中的一種選擇
Rack
但 Rails 跟 web server 怎麼溝通的?在 Ruby 裡面有一套機制叫做 Rack 的 Ruby protocol 就是由此而生
如果把這個 protocol 想像成是人在溝通,會像是這樣:
在 Ruby 裡面,看起來像是這樣
跟圖上面的不同,body 其實需要是一個 eachable 的 object,是一個可以迭代的物件
所以其實 Rack 就只是大家同意的一個 protocol 而已
Conventions for Rack App
web server 會準備一個 hash(env hash) 給 app server,其中最重要的一條 convention 就是你的 app server 需要有一個 call 的方法可以呼叫
一般來說為了讓 app 可以正常運作,我們需要跑 nginx / apach 這種 web server 起來接收 request,但 Ruby 有內建的 script 可以用 rackup
,他會去找 config.ru 這個檔案
1 | # app.rb |
但隨著行為變複雜,我們可能想再這個 app 前面做一些處理,比方說為了有 Redirect 的功能,我在 app server 外面再包一層 Redirect 的 middleware
1 | class Redirect |
這時候我們可能會這樣寫
1 | # config.ru |
而又有一個特殊的 DSL 可以用在這裡
1 | # config.ru |
這裡需要特別注意執行順序,request => Redirect => HelloWorld => Redirect
備註:
從龍哥的 文章 可以更清楚知道他們的執行順序:
Rails
那在 Rails 裡面又是什麼情況?
我們可以看到 Rails 裡面有 config.ru
1 | require ::File.expand_path('config/environment', __dir__) |
所以不管 Rails.application 是什麼,他一定是一個 Rack app,所以可以測試看看
1 | > rails c |
而在 Rails 裡面如果要拿掉 middleware 是用其他方法
1 | module My |
如果看 rails 的 middleware 會發現最後一個是 routes,所以 routes 也是一個 rack app
1 | > rails middleware |
所以可用前面一樣的方式來跑跑看
1 | > rails c |
這個 routes 的 rack app 的作用就是把 request 導向正確的 controller
1 | Rails.application.routes.draw do |
如果把 routes 的 call 展開會像是:
1 | class MyRoutes |
就是從上面透過 config/routes.rb 的設定進入到 controller 裡面
Thoughts
透過這個短短的 talk 讓我對於一些基礎知識更加根深蒂固,另外也終於慢慢覺得 Ruby 的黑魔法總算是一層一層的撥開面紗了,希望可以有更多這種 talk 或者未來自己有能力可以給出這種 talk!