आज की पोस्ट में, हम सीखेंगे कि एक ऐसा रेल्स ऐप कैसे बनाया जाए जो मल्टीपल सबडोमेन को सपोर्ट कर सके। मान लीजिए हमारे पास एक गेमिंग वेबसाइट funkygames.co है और हम app.funkygames.co, api.funkygames.co, और dev.funkygames.co जैसे मल्टीपल सबडोमेन को एक ही रेल्स एप्लिकेशन के साथ सपोर्ट करना चाहते हैं। हम यह सुनिश्चित करना चाहते हैं कि सभी सबडोमेन के लिए उचित ऑथेंटिकेशन हो और कोई डुप्लिकेट रूट न हों।
पूर्वापेक्षाएँ
इस पोस्ट के लिए, मैं मान रहा हूँ कि आपने सभी सबडोमेन के लिए उपयुक्त DNS रिकॉर्ड सेट कर लिए हैं जो रेल्स ऐप को पॉइंट करते हैं। हम इस पोस्ट में केवल रेल्स पक्ष को कवर करेंगे।
मल्टीपल सबडोमेन को संभालना
रेल्स आने वाले अनुरोधों को संभालने और उन्हें विशिष्ट कंट्रोलर एक्शन में मैप करने के लिए routes.rb फ़ाइल का उपयोग करता है। एक साधारण ऐप में, routes.rb में हर मैपिंग एक रूट को कंट्रोलर एक्शन से इस प्रकार मैप करती है:
get '/games/:id', to: 'games#show'
इस दृष्टिकोण के साथ, हमारे routes.rb फ़ाइल में परिभाषित सभी एंडपॉइंट सभी सबडोमेन पर लागू होते हैं। इसलिए app.funkygames.co/games/1 और api.funkygames.co/games/1 दोनों को यह रूट संभालेगा। हालांकि, हम केवल app सबडोमेन से आने वाले अनुरोध को इस रूट द्वारा संभालना चाहते हैं। api सबडोमेन का उपयोग केवल API रूट के लिए किया जाना है। हम रूट्स में कुछ नियम जोड़ेंगे ताकि वे केवल तभी संभाले जाएं जब आने वाले अनुरोध के लिए एक विशिष्ट नियम पूरा हो।
रेल्स रूटिंग एक constraints हेल्पर मेथड प्रदान करता है जो दिए गए रूट के लिए अतिरिक्त नियम निर्दिष्ट कर सकता है।
get '/games/:id', to: 'games#show', constraints: { subdomain: 'app' }
यह सुनिश्चित करेगा कि यदि अनुरोध app.funkygames.co/games/1 से आ रहा है, तो इसे GamesController के शो एक्शन द्वारा संभाला जाएगा। 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
मल्टीपल सबडोमेन के लिए रूट्स को परिभाषित करने के लिए, हमें बस अपनी routes.rb फ़ाइल में मल्टीपल constraints ब्लॉक जोड़ने होंगे।
constraints subdomain: 'app' do ... end
constraints subdomain: 'api' do ... end
constraints subdomain: 'dev' do ... end
आंतरिक कार्यप्रणाली
रेल्स रूटिंग रिक्वेस्ट कंस्ट्रेंट्स और सेगमेंट कंस्ट्रेंट्स प्रदान करता है। सेगमेंट कंस्ट्रेंट्स रिक्वेस्ट पथ पर नियम जोड़ते हैं जबकि रिक्वेस्ट कंस्ट्रेंट्स आने वाले अनुरोध पर शर्तें जोड़ते हैं। रिक्वेस्ट कंस्ट्रेंट में हैश की Request ऑब्जेक्ट पर एक मेथड होनी चाहिए जो स्ट्रिंग लौटाती हो और मान अपेक्षित मान होना चाहिए।
constraints subdomain: 'app' do ... end
उपरोक्त मामले में, हम Request ऑब्जेक्ट पर subdomain मेथड का उपयोग कर रहे हैं और इसका मिलान app, api या dev जैसे स्ट्रिंग से कर रहे हैं।
अधिक विवरण के लिए, रेल्स रूटिंग गाइड देखें।
मल्टी-लेवल सबडोमेन को संभालना
मान लीजिए हम अपने स्टेजिंग वातावरण के लिए 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 के लिए भी काम करेगा।
सत्र प्रबंधन
अब जब रूट्स मल्टीपल सबडोमेन से आने वाले अनुरोधों को संभालने के लिए परिभाषित हैं, तो हमें सभी सबडोमेन के लिए ऑथेंटिकेशन का ध्यान रखना होगा। हम इसे दो तरीकों से कर सकते हैं—हम या तो सभी सबडोमेन में समान यूजर सत्र का उपयोग करने की अनुमति दे सकते हैं, या अलग-अलग सबडोमेन के लिए अलग-अलग सत्र रख सकते हैं।
ऑथेंटिकेशन संक्षेप में
रेल्स डिफ़ॉल्ट रूप से यूजर सत्र कुंजी को स्टोर करने के लिए कुकीज़ का उपयोग करता है। एक बार जब यूजर लॉग इन करता है, तो यूजर की सत्र जानकारी हमारी पसंद के सत्र स्टोर में संग्रहीत होती है और सत्र कुंजी ब्राउज़र में कुकी के रूप में संग्रहीत होती है। इसलिए अगली बार जब यूजर हमारी वेबसाइट पर जाता है, तो वही सत्र कुकी ब्राउज़र से सर्वर को भेजी जाती है और सर्वर यह तय करता है कि यूजर लॉग इन है या नहीं, इस आधार पर कि आने वाली सत्र कुकी के लिए सत्र मौजूद है या नहीं।
रेल्स ऐप में सत्र के लिए डिफ़ॉल्ट कॉन्फ़िगरेशन इस तरह दिखता है:
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 के रूप में पास करके, हम मूल रूप से रेल्स को एप्लिकेशन के टॉप-लेवल डोमेन जैसे funkygames.co पर सत्र कुकी सेट करने के लिए कह रहे हैं, बजाय अनुरोध होस्ट के जिसमें व्यक्तिगत सबडोमेन शामिल हो सकते हैं। एक बार जब हम ऐसा करते हैं, तो सत्र को विभिन्न सबडोमेन के बीच साझा किया जा सकता है।
हम
domainsविकल्प में डोमेन की एक सूची को सरणी प्रारूप में भी पास कर सकते हैं ताकि मल्टीपल डोमेन को सपोर्ट किया जा सके।
एक और विकल्प है जिसे सभी सबडोमेन के लिए कुकीज़ को ठीक से सेट करने के लिए कॉन्फ़िगर करने की आवश्यकता है। यह tld_length विकल्प है। domain: :all का उपयोग करते समय, यह विकल्प निर्दिष्ट कर सकता है कि डोमेन को कैसे पार्स किया जाए ताकि कुकीज़ सेट करते समय डोमेन के TLD की व्याख्या की जा सके। हमारे मामले में, app.funkygames.co के लिए, हमें tld_length को 2 पर सेट करना चाहिए ताकि रेल्स कुकीज़ सेट करते समय 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 त्रुटियां होती हैं। रेल्स इंटीग्रेशन टेस्ट एक host! हेल्पर प्रदान करते हैं जो टेस्ट फ़ाइल के भीतर किए गए सभी अनुरोधों के लिए उचित सबडोमेन सेट कर सकता है।
# रेल्स इंटीग्रेशन टेस्ट में सबडोमेन कॉन्फ़िगर करना
setup do
host! 'dev.example.com'
end
# RSpec रिक्वेस्ट स्पेक्स में सबडोमेन कॉन्फ़िगर करना
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 जैसे टूल का भी उपयोग कर सकते हैं।
कंस्ट्रेंट-आधारित सबडोमेन रूटिंग के साथ समस्याएं
हालांकि कंस्ट्रेंट-आधारित सबडोमेन रूटिंग ज्यादातर मामलों में काम करती है, यह कुछ स्थितियों में परेशानी का कारण बन सकती है।
बाहरी API के साथ काम करना
जब हम थर्ड-पार्टी API के साथ काम कर रहे होते हैं और इंटीग्रेशन बना रहे होते हैं, तो स्थानीय विकास TLD जैसे .local या .dev की अनुमति नहीं होती है। हमें ngrok जैसे टूल का उपयोग करना पड़ता है। ऐसे मामलों में सबडोमेन-आधारित रूटिंग काम नहीं करती है और हमें कुछ रूट्स को व्हाइटलिस्ट करना पड़ता है ताकि वे ngrok के माध्यम से भी एक्सेसिबल हों।
सबडोमेन कंस्ट्रेंट्स के बाहर के रूट्स
कुछ रूट्स को सबडोमेन कंस्ट्रेंट्स के अंदर नहीं रखा जा सकता। एक विशिष्ट उदाहरण healthcheck या ping एंडपॉइंट्स हैं। यदि हम अपने रेल्स ऐप के सामने लोड बैलेंसर का उपयोग कर रहे हैं, तो लोड बैलेंसर को समय-समय पर जांचने की आवश्यकता होती है कि ऐप चालू है या नहीं। ऐसे मामलों में उपयोग किया जाने वाला healthcheck एंडपॉइंट सबडोमेन कंस्ट्रेंट्स के तहत नहीं हो सकता क्योंकि लोड बैलेंसर को शायद अनुरोध होस्ट का ज्ञान न हो।
रूट रूट की अनुपस्थिति
रेल्स में एक विशेष root रूट होता है जो मूल रूप से एप्लिकेशन का डिफ़ॉल्ट रूट होता है। यदि कोई अन्य रूट अनुरोध से मेल नहीं खाता है, तो root रूट का उपयोग किया जाता है। जब हमारे सभी रूट्स किसी एक सबडोमेन के तहत होते हैं, तो ऐसी स्थितियां हो सकती हैं जहां हमारे पास कोई root रूट बिल्कुल परिभाषित न हो। कुछ जेम्स root रूट की उपस्थिति पर निर्भर हो सकते हैं और हमें तदनुसार चेक और बैलेंस जोड़ने की आवश्यकता होती है।
निष्कर्ष
इस पोस्ट में, हमने बहुत कम कॉन्फ़िगरेशन लाइनों के साथ मल्टीपल सबडोमेन के साथ एक रेल्स ऐप सेट किया। हमने यह भी देखा कि सबडोमेन को स्थानीय रूप से और विभिन्न वातावरणों के साथ कैसे सेट किया जाए, साथ ही मल्टीपल सबडोमेन के लिए प्रभावी टेस्ट लिखने के टिप्स भी। रेल्स द्वारा प्रदान की गई प्लंबिंग के साथ, मल्टीपल सबडोमेन के साथ एक रेल्स ऐप को सेट अप और टेस्ट करना आसान हो जाता है।
P.S. यदि आप रूबी मैजिक पोस्ट को प्रेस से निकलते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!