Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การสร้างแอป Rails ที่มีโดเมนย่อยหลายโดเมน

ในโพสต์ของวันนี้ เราจะมาเรียนรู้วิธีสร้างแอป Rails ที่สามารถรองรับหลายโดเมนย่อยได้ สมมติว่าเรามีเว็บไซต์เกม funkygames.co และเราต้องการสนับสนุนโดเมนย่อยหลายโดเมน เช่น app.funkygames.co , api.funkygames.co และ dev.funkygames.co ด้วยแอปพลิเคชั่น Rails เดียว เราต้องการให้แน่ใจว่าได้ดำเนินการตรวจสอบสิทธิ์ที่ถูกต้องสำหรับโดเมนย่อยทั้งหมด และไม่มีเส้นทางที่ซ้ำกัน

เราจะใช้โครงสร้างการกำหนดเส้นทางที่มีประสิทธิภาพของ Rails เพื่อรองรับโดเมนย่อยหลายโดเมนในแอปพลิเคชันของเรา นอกจากนี้เรายังจะตั้งค่าโดเมนย่อยภายในเครื่องและเขียนการทดสอบสำหรับโดเมนย่อยหลายโดเมน

ข้อกำหนดเบื้องต้น

สำหรับจุดประสงค์ของโพสต์นี้ ฉันคิดว่าคุณได้ตั้งค่าระเบียน DNS ที่เหมาะสมสำหรับโดเมนย่อยทั้งหมดเพื่อชี้ไปที่แอป Rails เราจะจัดการกับเรื่อง Rails เท่านั้นในโพสต์นี้

การจัดการหลายโดเมนย่อย

Rails ใช้ routes.rb ไฟล์เพื่อจัดการคำขอที่เข้ามาและจับคู่กับการดำเนินการควบคุมเฉพาะ ในแอปที่ไม่สำคัญ ทุกแผนที่ใน routes.rb แมปเส้นทางไปยังการกระทำของตัวควบคุมดังนี้:

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

ด้วยวิธีการนี้ จุดปลายทั้งหมดที่กำหนดไว้ใน routes.rb . ของเรา fileare ใช้ได้กับโดเมนย่อยทั้งหมด ดังนั้น app.funkygames.co/games/1 เช่นเดียวกับ api.funkygames.co/games/1 จะจัดการโดยเส้นทางนี้ อย่างไรก็ตาม เราต้องการเพียงคำขอที่มาจาก app โดเมนย่อยที่จะจัดการโดยเส้นทางนี้ api โดเมนย่อยใช้สำหรับเส้นทาง API เท่านั้น เราจะเพิ่มกฎบางอย่างในเส้นทางเพื่อให้ได้รับการจัดการก็ต่อเมื่อเป็นไปตามกฎเฉพาะสำหรับคำขอที่เข้ามา

การกำหนดเส้นทาง Rails มี constraints วิธีช่วยเหลือซึ่งสามารถระบุกฎเพิ่มเติมสำหรับเส้นทางที่กำหนดได้

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

เพื่อให้แน่ใจว่าหากคำขอมาจาก app.funkygames.co/games/1 จะถูกจัดการโดย GamesController's แสดงการกระทำ คำขอใดๆ จากโดเมนย่อยอื่นๆ นอกเหนือจาก app จะไม่ถูกจัดการโดยเส้นทางนี้

การกำหนด constraints . จะยุ่งยากมาก แบบนี้ทุกเส้นทาง

  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' }

เราสามารถใช้รูปแบบบล็อกของ constraints ผู้ช่วยกำหนดเส้นทางหลายเส้นทางสำหรับโดเมนย่อยเดียว

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

ในการกำหนดเส้นทางสำหรับหลายโดเมนย่อย เราเพียงแค่เพิ่ม constraints . หลายรายการ บล็อกใน routes.rb . ของเรา ไฟล์.

constraints subdomain: 'app' do
  ...
end
 
constraints subdomain: 'api' do
  ...
end
 
constraints subdomain: 'dev' do
  ...
end

ภายใต้ประทุน

การกำหนดเส้นทาง Rails มีข้อจำกัดในคำขอและข้อจำกัดของเซ็กเมนต์ ข้อจำกัดของเซ็กเมนต์เพิ่มกฎบนเส้นทางคำขอ ในขณะที่ข้อจำกัดของคำขอจะเพิ่มเงื่อนไขในคำขอที่เข้ามา คีย์แฮชในข้อจำกัดคำขอจะต้องเป็นเมธอดใน Request วัตถุที่ส่งคืนสตริงและค่าต้องเป็นค่าที่คาดไว้

constraints subdomain: 'app' do
  ...
end

ในกรณีข้างต้น เรากำลังใช้ subdomain วิธีการใน Request วัตถุและจับคู่กับสตริงเช่น app , api หรือ dev .

สำหรับรายละเอียดเพิ่มเติม โปรดดูคู่มือการกำหนดเส้นทาง Rails

การจัดการโดเมนย่อยหลายระดับ

สมมติว่าเรากำลังใช้ app.staging.funkygames.co สำหรับสภาพแวดล้อมการแสดงละครของเรา ถ้าเรามีการตั้งค่าข้างต้น เราจะสังเกตเห็นได้อย่างรวดเร็วว่าคำขอทั้งหมดที่ควรจะเข้าถึง app โดเมนย่อยกำลังส่งคืน 404 หากเราดีบักสิ่งต่าง ๆ เพิ่มเติม เราจะสังเกตเห็นว่าข้อจำกัดของเราสำหรับโดเมนย่อยล้มเหลว

request.subdomain #=> app.staging

เราคาดว่าโดเมนย่อยจะส่งคืน app แต่จะส่งคืน app.staging . แทน . แน่นอน เราต้องการแก้ปัญหานี้โดยไม่ต้องเพิ่มโค้ดเฉพาะสภาพแวดล้อม! การแยกวิเคราะห์โดเมนย่อยของคำขอได้รับการจัดการโดย config.action_dispatch.tld_length ตัวเลือก. ค่าเริ่มต้นของการกำหนดค่านี้คือ 1 ซึ่งโดยทั่วไปจะสนับสนุนโดเมนย่อยหนึ่งระดับ เนื่องจากเรามีโดเมนย่อยสองระดับ เราจึงต้องตั้งค่าสำหรับ config.action_dispatch.tld_length ถึง 2.

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

เราสามารถตั้งค่าโดยใช้ตัวแปรสภาพแวดล้อมเพื่อให้เราสามารถใช้รหัสเดียวกันในการแสดงละครและในสภาพแวดล้อมการผลิตได้ ตอนนี้ การตั้งค่าการกำหนดเส้นทางของเราจะใช้ได้กับ app.staging.funkygames.co เช่นกัน

การจัดการเซสชัน

ตอนนี้มีการกำหนดเส้นทางเพื่อจัดการคำขอที่มาจากหลายโดเมนย่อย เราจำเป็นต้องดูแลการตรวจสอบสิทธิ์สำหรับโดเมนย่อยทั้งหมด เราทำได้ 2 วิธี คืออนุญาตให้ใช้เซสชันผู้ใช้เดียวกันในโดเมนย่อยทั้งหมด หรืออาจมีเซสชันแยกกันสำหรับโดเมนย่อยแยกกัน

รับรองความถูกต้องโดยย่อ

Rails ใช้คุกกี้เพื่อจัดเก็บคีย์เซสชันของผู้ใช้ตามค่าเริ่มต้น เมื่อผู้ใช้เข้าสู่ระบบ ข้อมูลเซสชันของผู้ใช้จะถูกเก็บไว้ในที่เก็บเซสชันที่เราเลือก และคีย์เซสชันจะถูกจัดเก็บเป็นคุกกี้ในเบราว์เซอร์ ดังนั้นในครั้งต่อไปที่ผู้ใช้เยี่ยมชมเว็บไซต์ของเรา คุกกี้เซสชันเดียวกันจะถูกส่งจากเบราว์เซอร์ไปยังเซิร์ฟเวอร์ และเซิร์ฟเวอร์จะตัดสินว่าผู้ใช้เข้าสู่ระบบหรือไม่ขึ้นอยู่กับว่ามีเซสชันสำหรับคุกกี้เซสชันขาเข้าหรือไม่

การกำหนดค่าเริ่มต้นสำหรับเซสชันจะมีลักษณะเช่นนี้ในแอป Rails:

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

คีย์ _funkygames_session จะถูกใช้เป็นชื่อของคุกกี้เซสชันและค่าจะเป็นรหัสเซสชัน

คุกกี้ไพรเมอร์

โดยค่าเริ่มต้น เบราว์เซอร์จะตั้งค่าคุกกี้ในโดเมนของคำขอ ดังนั้นหากเรากดแอปพลิเคชันของเราจาก app.funkygames.co จากนั้นเซสชันคุกกี้จะถูกตั้งค่าเทียบกับ app.funkygames.co . โดเมนย่อยแต่ละโดเมนจะตั้งค่าคุกกี้เซสชันของตัวเอง ดังนั้นเซสชันของผู้ใช้จะไม่ถูกแชร์ข้ามโดเมนย่อยโดยค่าเริ่มต้น

การแบ่งปันเซสชันระหว่างโดเมนย่อยต่างๆ

หากเราต้องการแบ่งปันเซสชันของผู้ใช้ข้ามโดเมนย่อย เราจะต้องตั้งค่าคุกกี้ของเซสชันใน funkygames.co โดเมนเองเพื่อให้โดเมนย่อยทั้งหมดสามารถเข้าถึงได้ สามารถทำได้โดยส่ง domain ตัวเลือกไปยังการตั้งค่าที่เก็บเซสชั่น

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

โดยส่ง domain เป็น :all โดยพื้นฐานแล้ว เราจะบอกให้ Rails ตั้งค่าเซสชันคุกกี้ในโดเมนระดับบนสุดของแอปพลิเคชัน เช่น funkygames.co แทนที่จะอยู่บนโฮสต์คำขอซึ่งอาจรวมถึงโดเมนย่อยแต่ละรายการ เมื่อเราทำสิ่งนี้แล้ว เซสชันสามารถแชร์ระหว่างโดเมนย่อยต่างๆ ได้

นอกจากนี้เรายังสามารถส่งรายชื่อโดเมนไปยัง domains ในรูปแบบอาร์เรย์เพื่อรองรับหลายโดเมน

มีอีกหนึ่งตัวเลือกที่ต้องกำหนดค่าเพื่อตั้งค่าคุกกี้อย่างถูกต้องสำหรับโดเมนย่อยทั้งหมด มันคือ tld_length ตัวเลือก. เมื่อใช้ domain: :all ตัวเลือกนี้สามารถระบุวิธีแยกวิเคราะห์โดเมนเพื่อตีความ TLD ของโดเมนได้ ในกรณีของเรา สำหรับ app.funkygames.co เราควรตั้งค่า tld_length ถึง 2 สำหรับ Rails เพื่อตีความ TLD เป็น funkygames.co เมื่อตั้งค่าคุกกี้ ดังนั้นการกำหนดค่าที่เก็บเซสชันขั้นสุดท้ายสำหรับโดเมนย่อยหลายโดเมนจึงมีลักษณะดังนี้:

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

tld_length ตัวเลือกจากที่เก็บเซสชันแตกต่างจาก config.action_dispatch.tld_length พูดคุยกันก่อนหน้านี้

การทดสอบการเขียนสำหรับโดเมนย่อยหลายโดเมน

เนื่องจากเส้นทางเป็นโดเมนย่อยเฉพาะ ข้อกำหนดคำขอหรือการทดสอบการรวมจึงส่งผลให้เกิดข้อผิดพลาด 404 หากคำขอทดสอบไม่มีโดเมนย่อยที่เหมาะสม การทดสอบการรวม Rails ให้ host! helper ซึ่งสามารถตั้งค่าโดเมนย่อยที่เหมาะสมสำหรับคำขอทั้งหมดที่ทำในไฟล์ทดสอบ

# 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

หลังจากนี้ คำขอจะถูกส่งไปยังการดำเนินการของตัวควบคุมอย่างถูกต้องตามการกำหนดเส้นทางของโดเมนย่อยใน routes.rb ไฟล์.

โปรดทราบว่าโดเมนไม่สำคัญที่นี่ เฉพาะโดเมนย่อยที่เหมาะสมตามรหัสที่เรากำลังทดสอบเท่านั้น

การตั้งค่าโดเมนย่อยหลายโดเมนสำหรับการพัฒนา

มีหลายวิธีในการตั้งค่าโดเมนย่อยในเครื่อง วิธีที่ง่ายที่สุดคือการแก้ไข /etc/hosts ไฟล์.

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

เพื่อให้แน่ใจว่าการตั้งค่าโดเมนย่อยจะทำงานในสภาพแวดล้อมภายในเครื่อง เรายังใช้เครื่องมือต่างๆ เช่น pow เพื่อจัดการโดเมนย่อยในเครื่องได้

Gotchas ที่มีการกำหนดเส้นทางโดเมนย่อยตามข้อจำกัด

แม้ว่าการกำหนดเส้นทางโดเมนย่อยตามข้อจำกัดจะได้ผลในกรณีส่วนใหญ่ แต่ก็อาจทำให้ลำบากในบางสถานการณ์

การจัดการกับ API ภายนอก

เมื่อเรากำลังทำงานกับ API ของบุคคลที่สามและการผสานรวมการสร้าง TLD ของการพัฒนาในพื้นที่ เช่น .local หรือ .dev ไม่ได้รับอนุญาต. เราต้องใช้เครื่องมือเช่น ngrok การกำหนดเส้นทางตามโดเมนย่อยไม่ทำงานในกรณีดังกล่าว และเราต้องอนุญาตพิเศษบางเส้นทางเพื่อให้สามารถเข้าถึงได้ผ่าน ngrok เช่นกัน

เส้นทางนอกข้อจำกัดของโดเมนย่อย

ไม่สามารถวางเส้นทางบางเส้นทางภายในข้อจำกัดของโดเมนย่อย ตัวอย่างทั่วไปคือ healthcheck หรือ ping ปลายทาง หากเราใช้ตัวโหลดบาลานซ์หน้าแอพ Rails ตัวโหลดบาลานซ์จะต้องตรวจสอบเป็นระยะๆ ว่าแอพนั้นทำงานหรือไม่ healthcheck ปลายทางที่ใช้ในกรณีดังกล่าวต้องไม่อยู่ภายใต้ข้อจำกัดของโดเมนย่อย เนื่องจากตัวโหลดบาลานซ์มักจะไม่มีความรู้เกี่ยวกับโฮสต์คำขอ

ไม่มีรูทเส้นทาง

Rails มี root . พิเศษ เส้นทางซึ่งเป็นเส้นทางเริ่มต้นของแอปพลิเคชันโดยพื้นฐาน หากไม่มีเส้นทางอื่นที่ตรงกับคำขอ root มีการใช้เส้นทาง เมื่อเรามีเส้นทางทั้งหมดภายใต้โดเมนย่อย อาจมีสถานการณ์ที่เราไม่มี root เส้นทางที่กำหนดไว้เลย อัญมณีบางชนิดอาจขึ้นอยู่กับการมีอยู่ของ root เส้นทางและเราจำเป็นต้องเพิ่มการตรวจสอบและยอดคงเหลือตามนั้น

บทสรุป

ในโพสต์นี้ เราตั้งค่าแอป Rails ที่มีโดเมนย่อยหลายโดเมนโดยมีการกำหนดค่าเพียงไม่กี่บรรทัด เรายังได้เห็นวิธีตั้งค่าโดเมนย่อยภายในเครื่องและสภาพแวดล้อมที่แตกต่างกัน พร้อมเคล็ดลับในการเขียนการทดสอบที่มีประสิทธิภาพสำหรับโดเมนย่อยหลายโดเมน ด้วยระบบประปาที่ Rails จัดหาให้ การติดตั้งและทดสอบแอป Rails ที่มีโดเมนย่อยหลายโดเมนจึงกลายเป็นเรื่องง่าย

ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!