ในโพสต์ของวันนี้ เราจะมาเรียนรู้วิธีสร้างแอป 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 ของเราและไม่พลาดแม้แต่โพสต์เดียว!