Computer >> Máy Tính >  >> Lập trình >> Ruby

Xây dựng ứng dụng Rails với nhiều tên miền phụ

Trong bài đăng hôm nay, chúng ta sẽ tìm hiểu cách xây dựng một ứng dụng Rails có thể hỗ trợ nhiều tên miền. Giả sử rằng chúng ta có một trang web trò chơi funkygames.co và chúng tôi muốn hỗ trợ nhiều miền phụ, chẳng hạn như app.funkygames.co , api.funkygames.codev.funkygames.co với một ứng dụng Rails duy nhất. Chúng tôi muốn đảm bảo rằng xác thực thích hợp được thực hiện cho tất cả các miền phụ và không có các tuyến trùng lặp.

Chúng tôi sẽ sử dụng các cấu trúc định tuyến mạnh mẽ của Rails để hỗ trợ nhiều miền phụ trong ứng dụng của chúng tôi. Chúng tôi cũng sẽ thiết lập cục bộ các miền phụ và viết các bài kiểm tra cho nhiều miền phụ.

Điều kiện tiên quyết

Với mục đích của bài đăng này, tôi giả sử rằng bạn đã thiết lập các bản ghi DNS thích hợp cho tất cả các miền phụ để trỏ đến ứng dụng Rails. Chúng tôi sẽ chỉ giải quyết khía cạnh Rails của những thứ trong bài đăng này.

Xử lý nhiều tên miền phụ

Rails sử dụng routes.rb tệp để xử lý các yêu cầu đến và ánh xạ chúng tới các hành động cụ thể của bộ điều khiển. Trong một ứng dụng tầm thường, mọi ánh xạ trong routes.rb ánh xạ một lộ trình đến một hành động của bộ điều khiển như sau:

  get '/games/:id', to: 'games#show'

Với cách tiếp cận này, tất cả các điểm cuối được xác định trong routes.rb của chúng tôi tệp có thể áp dụng cho tất cả các miền phụ. Vì vậy, app.funkygames.co/games/1 cũng như api.funkygames.co/games/1 sẽ được xử lý bởi tuyến đường này. Tuy nhiên, chúng tôi chỉ muốn yêu cầu đến từ ứng dụng app tên miền phụ sẽ được xử lý bởi tuyến đường này. api tên miền phụ chỉ được sử dụng cho các tuyến API. Chúng tôi sẽ thêm một số quy tắc vào các tuyến để chúng chỉ được xử lý nếu một quy tắc cụ thể được đáp ứng cho yêu cầu đến.

Định tuyến đường ray cung cấp một constraints phương thức trợ giúp có thể chỉ định các quy tắc bổ sung cho tuyến đường đã cho.

  get '/games/:id', to: 'games#show', constraints: { subdomain: 'app' }

Điều này sẽ đảm bảo rằng nếu yêu cầu đến từ app.funkygames.co/games/1 , nó sẽ được xử lý bởi GamesController's hiện hành động. Bất kỳ yêu cầu nào từ các miền phụ khác ngoài ứng dụng app sẽ không được xử lý bởi tuyến đường này.

Việc xác định constraints sẽ trở nên rất cồng kềnh như thế này cho mỗi và mọi tuyến đường.

  get '/games/:id', to: 'games#show', constraints: { subdomain: 'app' }
  get '/games/list', to: 'games#list', constraints: { subdomain: 'app' }
  post '/games/start', to: 'games#start', constraints: { subdomain: 'app' }

Chúng ta có thể sử dụng dạng khối của constraints trình trợ giúp để xác định nhiều tuyến cho một miền phụ.

Tên miền phụ
  constraints subdomain: 'app' do
    get '/games/:id', to: 'games#show'
    get '/games/list', to: 'games#list'
    post '/games/start', to: 'games#start'
  end

Để xác định các tuyến đường cho nhiều miền phụ, chúng ta chỉ cần thêm nhiều constraints các khối trong routes.rb của chúng tôi tệp.

Tên miền phụ
constraints subdomain: 'app' do
  ...
end
 
constraints subdomain: 'api' do
  ...
end
 
constraints subdomain: 'dev' do
  ...
end

Nâng cao

Định tuyến Rails cung cấp các ràng buộc yêu cầu và ràng buộc phân đoạn. Các ràng buộc phân đoạn thêm các quy tắc trên đường dẫn yêu cầu trong khi các ràng buộc yêu cầu thêm các điều kiện cho yêu cầu đến. Khóa băm trong một ràng buộc yêu cầu cần phải là một phương thức trên Request đối tượng trả về một chuỗi và giá trị cần phải là giá trị mong đợi.

Tên miền phụ
constraints subdomain: 'app' do
  ...
end

Trong trường hợp trên, chúng tôi đang sử dụng subdomain trên Request và đối sánh nó với một chuỗi như app , api hoặc dev .

Để biết thêm chi tiết, hãy tham khảo hướng dẫn định tuyến Rails.

Xử lý tên miền phụ nhiều cấp

Giả sử chúng tôi đang sử dụng app.staging.funkygames.co cho môi trường dàn dựng của chúng tôi. Nếu chúng tôi có thiết lập ở trên, chúng tôi sẽ nhanh chóng nhận thấy rằng tất cả các yêu cầu được cho là sẽ truy cập vào ứng dụng app miền phụ đang trả về 404. Nếu chúng tôi gỡ lỗi thêm, chúng tôi sẽ nhận thấy rằng ràng buộc của chúng tôi cho miền phụ không thành công.

request.subdomain #=> app.staging

Chúng tôi mong đợi miền phụ trả về ứng dụng app nhưng thay vào đó, nó trả về app.staging . Tất nhiên, chúng tôi muốn giải quyết vấn đề này mà không cần thêm mã dành riêng cho môi trường! Việc phân tích cú pháp của tên miền phụ của yêu cầu được quản lý bởi config.action_dispatch.tld_length quyền mua. Giá trị mặc định của cấu hình này là 1, về cơ bản hỗ trợ một cấp miền phụ. Vì chúng tôi có hai miền phụ cấp, chúng tôi cần đặt giá trị cho config.action_dispatch.tld_length đến 2.

# config/application.rb
config.action_dispatch.tld_length = Integer(ENV['TLD_LENGTH'] || 1)

Chúng ta có thể đặt nó bằng cách sử dụng một biến môi trường để chúng ta có thể sử dụng cùng một đoạn mã trong hệ thống cũng như trong môi trường sản xuất. Bây giờ, thiết lập định tuyến của chúng tôi sẽ hoạt động cho app.staging.funkygames.co nữa.

Quản lý phiên

Bây giờ các tuyến được xác định để xử lý các yêu cầu đến từ nhiều miền phụ, chúng tôi cần quan tâm đến việc xác thực cho tất cả các miền phụ. Chúng tôi có thể làm điều này theo hai cách — chúng tôi có thể cho phép sử dụng cùng một phiên người dùng trên tất cả các miền phụ hoặc chúng tôi có thể có các phiên riêng biệt cho các miền phụ riêng biệt.

Xác thực trong Nutshell

Rails sử dụng cookie để lưu trữ khóa phiên của người dùng theo mặc định. Khi người dùng đăng nhập, thông tin phiên của người dùng được lưu trữ trong kho lưu trữ phiên mà chúng tôi chọn và khóa phiên được lưu trữ dưới dạng cookie trong trình duyệt. Vì vậy, lần tiếp theo người dùng truy cập trang web của chúng tôi, cùng một cookie phiên được gửi từ trình duyệt đến máy chủ và máy chủ sẽ quyết định xem người dùng có đăng nhập hay không dựa trên việc phiên đó có tồn tại cookie phiên đến hay không.

Cấu hình mặc định cho phiên này trông giống như thế này trong ứng dụng Rails:

Rails.application.config.session_store :cookie_store, key: "_funkygames_session"

Chìa khóa _funkygames_session sẽ được sử dụng làm tên của cookie phiên và giá trị của nó sẽ là id phiên.

Lớp lót cookie

Theo mặc định, cookie được đặt bởi trình duyệt trên miền của yêu cầu. Vì vậy, nếu chúng tôi đang truy cập ứng dụng của mình từ app.funkygames.co thì cookie phiên sẽ được đặt dựa trên app.funkygames.co . Mỗi miền phụ sẽ đặt cookie phiên của riêng mình, do đó, phiên của người dùng sẽ không được chia sẻ trên các miền phụ theo mặc định.

Phiên chia sẻ giữa các miền phụ khác nhau

Nếu chúng tôi muốn chia sẻ phiên người dùng trên các miền phụ, chúng tôi cần đặt cookie phiên trên funkygames.co miền chính nó để tất cả các miền phụ có thể truy cập nó. Điều này có thể đạt được bằng cách chuyển domain tùy chọn cài đặt cửa hàng phiên.

Rails.application.config.session_store :cookie_store, key: "_funkygames_session", domain: :all

Bằng cách chuyển domain dưới dạng :all , về cơ bản chúng tôi đang yêu cầu Rails đặt cookie phiên trên miền cấp cao nhất của ứng dụng, chẳng hạn như funkygames.co thay vì trên máy chủ yêu cầu có thể bao gồm các tên miền phụ riêng lẻ. Khi chúng tôi thực hiện việc này, phiên có thể được chia sẻ giữa các miền phụ khác nhau.

Chúng tôi cũng có thể chuyển danh sách tên miền sang domains ở định dạng mảng để hỗ trợ nhiều miền.

Có một tùy chọn nữa cần được định cấu hình để đặt đúng cookie cho tất cả các miền phụ. Đây là tld_length quyền mua. Khi sử dụng domain: :all , tùy chọn này có thể chỉ định cách phân tích cú pháp miền để diễn giải TLD của miền. Trong trường hợp của chúng tôi, đối với app.funkygames.co , chúng ta nên đặt tld_length thành 2 để Rails diễn giải TLD là funkygames.co khi thiết lập cookie. Vì vậy, cấu hình cửa hàng phiên cuối cùng cho nhiều tên miền phụ trông giống như sau:

Rails.application.config.session_store :cookie_store,
                                       key: "_funkygames_session",
                                       domain: :all,
                                       tld_length: 2

tld_length tùy chọn từ cửa hàng phiên khác với config.action_dispatch.tld_length đã thảo luận trước đó.

Kiểm tra Viết cho Nhiều Tên miền Phụ

Vì các tuyến là miền phụ cụ thể, thông số kỹ thuật yêu cầu hoặc kiểm tra tích hợp dẫn đến lỗi 404 nếu yêu cầu kiểm tra không có miền phụ thích hợp. Kiểm tra tích hợp Rails cung cấp một máy chủ lưu trữ host! trình trợ giúp có thể đặt miền phụ thích hợp cho tất cả các yêu cầu được thực hiện trong tệp thử nghiệm.

# Configuring subdomain in Rails integration tests
setup do
  host! 'dev.example.com'
end
 
# # Configuring subdomain in RSpec request specs
before do
 host! 'dev.example.com'
end

Sau đó, các yêu cầu sẽ được định tuyến chính xác đến các hành động của bộ điều khiển theo định tuyến tên miền phụ trong routes.rb tệp.

Lưu ý rằng miền không quan trọng ở đây, chỉ miền phụ thích hợp dựa trên mã mà chúng tôi đang kiểm tra mới quan trọng.

Thiết lập nhiều miền phụ cục bộ để phát triển

Có nhiều cách để thiết lập miền phụ cục bộ. Đơn giản nhất là chỉnh sửa /etc/hosts tệp.

127.0.0.1 dev.funkygames.local
127.0.0.1 app.funkygames.local
127.0.0.1 api.funkygames.local

Điều này đảm bảo rằng thiết lập miền phụ sẽ hoạt động trong môi trường cục bộ. Chúng tôi cũng có thể sử dụng các công cụ như pow để quản lý các miền phụ cục bộ.

Gotchas với Định tuyến Tên miền Phụ Dựa trên Ràng buộc

Mặc dù định tuyến tên miền phụ dựa trên ràng buộc hoạt động trong hầu hết các trường hợp, nhưng nó có thể là một vấn đề khó khăn trong một số trường hợp nhất định.

Xử lý các API bên ngoài

Khi chúng tôi làm việc với các API của bên thứ ba và tích hợp xây dựng, các TLD phát triển cục bộ như .local hoặc .dev không được cho phép. Chúng tôi phải sử dụng các công cụ như ngrok. Định tuyến dựa trên tên miền phụ không hoạt động trong những trường hợp như vậy và chúng tôi phải đưa vào danh sách trắng các tuyến đường nhất định để chúng cũng có thể truy cập được qua ngrok.

Các tuyến đường bên ngoài các ràng buộc của tên miền phụ

Các tuyến đường nhất định không thể được đặt bên trong các ràng buộc miền phụ. Ví dụ điển hình là healthcheck hoặc ping điểm cuối. Nếu chúng ta đang sử dụng bộ cân bằng tải trước ứng dụng Rails của mình, bộ cân bằng tải cần phải kiểm tra định kỳ xem ứng dụng có hoạt động hay không. healthcheck endpoint được sử dụng trong những trường hợp như vậy không được nằm trong các ràng buộc về tên miền phụ vì bộ cân bằng tải có thể hầu hết sẽ không có kiến ​​thức về máy chủ yêu cầu.

Sự vắng mặt của Tuyến gốc

Rails có một root đặc biệt route về cơ bản là route mặc định của ứng dụng. Nếu không có tuyến nào khác phù hợp với yêu cầu thì root tuyến đường được sử dụng. Khi chúng tôi có tất cả các tuyến đường của mình dưới bất kỳ miền phụ nào, thì có thể xảy ra trường hợp chúng tôi không có bất kỳ root nào route được xác định ở tất cả. Một số gem nhất định có thể phụ thuộc vào sự hiện diện của một root định tuyến và chúng tôi cần thêm séc và số dư cho phù hợp.

Kết luận

Trong bài đăng này, chúng tôi thiết lập một ứng dụng Rails với nhiều miền phụ với rất ít dòng cấu hình. Chúng tôi cũng đã xem cách thiết lập miền phụ cục bộ cũng như với các môi trường khác nhau, với các mẹo về cách viết các bài kiểm tra hiệu quả cho nhiều miền phụ. Với hệ thống ống nước do Rails cung cấp, việc thiết lập và thử nghiệm ứng dụng Rails với nhiều miền phụ trở nên dễ dàng.

Tái bút. Nếu bạn muốn đọc các bài đăng của Ruby Magic ngay khi chúng xuất hiện trên báo chí, hãy đăng ký bản tin Ruby Magic của chúng tôi và không bao giờ bỏ lỡ một bài đăng nào!