+5

Rails and Rack


Trong khi tìm hiểu và làm việc với rails, ắt hẳn chúng ta đều nghe tới khái niệm Rack.

Vậy Rack là gì ? Những request được gửi tới Rails app sẽ được xử lý như thế nào ?

Chào mọi người, hôm nay chúng ta sẽ tìm hiểu những vấn đề về Rack.

🍥

Rack

Rack được định nghĩa :

Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby

Có thể hiểu rằng Rack là một interface, một component đặt giữa webserver và các ruby framework, với mục đích làm cầu nối cho việc giao tiếp giữa webserverruby framework .


Request response with Rack

Khi một request được gửi đi từ browser, webserver(Puma, Unicorn, Webrick) sẽ nhận HTTP request này, sau đó chuyển tới cho Rack , Rack tiếp tục chuyển tới web app (Rails, Sinatra).

Web app xử lý request được gửi tới, tạo response trả về cho người dùng theo chiều ngược lại :

Vậy tại sao chúng ta cần Rack, tại sao các webserver không gửi thẳng request và nhận response từ các webapp mà phải thông qua Rack ?

Bởi việc sử dụng Rack để giao tiếp giữa webserver và webapp giúp ta có thể sử dụng nhiều webserver khác nhau với nhiều web application khác nhau. Ví dụ ta muốn dùng Puma thay vì Webrick, hay sử dụng Sinatra thay vì Rails đều được, bởi các webserver và webapp đều có thể giao tiếp với nhau thông qua Rack :

Deeper

Quay lại với request, khi được gửi từ browser tới webserver, webserver sẽ convert HTTP request thành một ruby hash: env, env có dạng giống như :

{"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/", "QUERY_STRING"=>"", "REMOTE_ADDR"=>"127.0.0.1", "REM
OTE_HOST"=>"localhost", "REQUEST_METHOD"=>"GET", "REQUEST_URI"=>"http://localhost:9292/", "SCRIPT_NAME"
=>"", "SERVER_NAME"=>"localhost", "SERVER_PORT"=>"9292", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWAR
E"=>"WEBrick/1.3.1 (Ruby/2.2.1/2015-02-26)", "HTTP_HOST"=>"localhost:9292", "HTTP_ACCEPT_LANGUAGE"=>"en
-US,en;q=0.8,de;q=0.6", "HTTP_CACHE_CONTROL"=>"max-age=0", "HTTP_ACCEPT_ENCODING"=>"gzip", "HTTP_ACCEPT
"=>"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "HTTP_USER_AGENT"=>"Mo
zilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.1
35 Safari/537.36", "rack.version"=>[1, 3], "rack.url_scheme"=>"http", "HTTP_VERSION"=>"HTTP/1.1", "REQU
EST_PATH"=>"/"}

Có thể chia env thành 3 phần :

  • Request headers :
  HTTP_HOST: "localhost:9292"
  HTTP_REFERER: "http://localhost:9292/"
  HTTP_ACCEPT_LANGUAGE: "en-US,en;q=0.8,de;q=0.6"
  HTTP_ACCEPT_ENCODING: "gzip"
  HTTP_USER_AGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36"
  HTTP_ACCEPT: "*/*"
  HTTP_VERSION: "HTTP/1.1"
  • Server info :
GATEWAY_INTERFACE: "CGI/1.1"
  PATH_INFO: "/"
  QUERY_STRING: ""
  REMOTE_ADDR: "127.0.0.1"
  REMOTE_HOST: "localhost"
  REQUEST_METHOD: "GET"
  REQUEST_URI: "http://localhost:9292/"
  SCRIPT_NAME: ""
  SERVER_NAME: "localhost"
  SERVER_PORT: "9292"
  • Rack info :
  rack.version: [1, 3]
  rack.url_scheme: "http"

Vậy biến env này được xử lý trong Rack như thế nào ?

Rack application sẽ nhận biến env này . Một Rack app cần có một method call, và trả về một mảng 3 phần tử :

  • The HTTP response code
  • A Hash of headers
  • The response body
class RackApp
  def call(env)
    #do some action according to the contents in the env hash
    result = action(env["PATH_INFO"])
    [200, { 'Content-Type' => 'text/html' }, result]
  end
end

Rack middleware

Mỗi Rack middleware là một rack app, với mục đích khác nhau, thực hiện những chức năng khác nhau với request được gửi tới hay response được trả về

In rails?

Rails is rack application, a final application được dựng bởi một loạt các middleware với các mục đích khác nhau. $ rake middleware sẽ đưa ra list các middleware trong app:

Một số middleware:

  • Rack::Sendfile: xử lý các static file dưới thư mục public
  • Rack::Runtime: thêm trường X-Runtime trong response header được trả về, chỉ ra khoảng thời gian xử lý cho request tương ứng
  • Rack::MethodOverride: đặt giá trị cho HTTP request method dựa trên input "_method" trong mỗi form được tạo ra bởi rails form helper
  • ActionDispatch::RequestId: gán một unique ID cho mỗi request tới
  • ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions: bắt các exception và hiển thị ra các custom page:
  • ActionDispatch::Callbacks: cung cấp các before/after callback
  • ActionDispatch::Flash: lưu flash, và xóa bỏ các flash cũ trong session
  • ActionDispatch::Session::CookieStore: lưu trữ session trong cookie
  • ActionDispatch::Cookies: lưu trữ cookie trong browser thông qua Set-Cookie header
  • Rack::Cache: hỗ trợ rails với HTTP Caching

request tới, các middleware lần lượt được gọi theo thứ tự, cuối cùng tới RailsApp::Application.routes, tìm ra controller tương ứng, và trả về response


Trên đây là một số tìm hiểu của mình về Rack, cám ơn mọi người đã theo dõi 💥


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí