Đang xảy ra gì behind the scenes của mọi Rails, Sinatra, và Ruby web framework khác?
Câu trả lời là Rack, component then chốt làm cho điều này possible.
Nhưng Rack chính xác là gì?
Rack là layer giữa framework (Rails) và application server (Puma).

Nó là chất keo cho phép chúng communicate.
Tại sao dùng Rack?
Chúng ta dùng Rack vì nó cho phép framework và server khác nhau interchangeable.
Chúng thành component plug-and-play.
Nghĩa là bạn dùng Puma với Rails, Sinatra và bất kỳ Rack-compatible framework nào. Không quan trọng framework hoặc server bạn dùng miễn chúng implement Rack interface.
Với Rack, mọi component làm việc của nó và mọi người happy!
Rack Middleware là gì?
Rack nằm giữa mọi web request và response.
Kết quả, nó act như guardian, deny access unwanted request, hoặc historian, track slow response.
Đó là Rack middleware!
Nhỏ Ruby program được gọi như phần của request-response cycle và có chance làm gì đó với nó.
Dùng cho gì?
- Logging
- Sessions
- Profiling (tìm request mất bao lâu complete)
- Caching
- Security (deny request dựa trên IP, hoặc limit # request)
- Serving static files (css, js, png…)
Đây khá useful và Rails dùng middleware tốt để implement một số functionality.
Bạn xem list middleware với rake middleware trong project Rails.
Bây giờ, Rack interface** tôi nói trước.
Trông như thế nào?
Cho bạn xem ví dụ…
Cách viết Rack Application riêng
Bạn học Rack hoạt động như nào bằng cách viết app riêng.
Làm nào!
Rack application là class với một method: call.
Trông như sau:
require 'rack'
handler = Rack::Handler::Thin
class RackApp
def call(env)
[200, {"Content-Type" => "text/plain"}, "Hello from Rack"]
end
end
handler.run RackApp.new
Code này start server port 8080 (thử đi!).
Array trả về là gì?
- HTTP status code (200)
- HTTP headers ("Content-Type")
- Nội dung ("Hello from Rack")
Nếu muốn truy cập request detail dùng argument env.
Như sau:
req = Rack::Request.new(env)
Các method available:
- path_info (/articles/1)
- ip (của user)
- user_agent (Chrome, Firefox, Safari…)
- request_method (get / post)
- body (nội dung)
- media_type (plain, json, html)
Bạn dùng info này xây dựng Rack application.
Ví dụ, deny access content nếu IP là 5.5.5.5.
Code:
require 'rack'
handler = Rack::Handler::Thin
class RackApp
def call(env)
req = Rack::Request.new(env)
if req.ip == "5.5.5.5"
[403, {}, ""]
else
[200, {"Content-Type" => "text/plain"}, "Hello from Rack"]
end
end
end
handler.run RackApp.new
Bạn đổi address thành 127.0.0.1 xem effect.
Nếu không work thử ::1, IPv6 localhost.
Cách viết & dùng Rack Middleware
Bây giờ:
Làm sao chain application và middleware làm việc cùng nhau?
Dùng Rack::Builder.
Cách hiểu tốt nhất là ví dụ.
Rack app của chúng ta:
require 'rack'
handler = Rack::Handler::Thin
class RackApp
def call(env)
req = Rack::Request.new(env)
[200, {"Content-Type" => "text/plain"}, "Hello from Rack - #{req.ip}"]
end
end
Middleware:
class FilterLocalHost
def initialize(app)
@app = app
end
def call(env)
req = Rack::Request.new(env)
if req.ip == "127.0.0.1" || req.ip == "::1"
[403, {}, ""]
else
@app.call(env)
end
end
end
Cách combine:
app = Rack::Builder.new do |builder| builder.use FilterLocalHost builder.run RackApp.new end handler.run app
Trong ví dụ này có hai Rack application:
- Một cho IP check (
FilterLocalHost) - Một cho app deliver content (HTML, JSON, etc)
Chú ý @app.call(env), đó là điều làm FilterLocalHost thành middleware.
Một trong hai chuyện xảy ra:
- Trả response, stop middleware chain
- Pass request với
@app.call(env)cho middleware tiếp, hoặc app
Bất kỳ phần nào Rack app, kể cả middleware, bạn thay đổi response.
Ví dụ:
class UpcaseAll
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
response.upcase!
[status, headers, response]
end
end
Đó chính là cách Rack hoạt động 🙂
Tóm tắt
Bạn đã học về Rack, interface điều khiển tương tác giữa Ruby web framework và server. Cũng học viết Rack app riêng để hiểu cách hoạt động.
Nếu có câu hỏi hoặc feedback hãy comment bên dưới.
Cảm ơn đã đọc!