For those who want to implement subdomain programmatically within your rails application, you can follow these few steps:
1. Generate Account Model
First, let’s create an Account model as a table to store subdomain names that we will accept in our application.
rails g model Account subdomain:string
From the above generator, it generates a migration file. We can then do rails db:migrate
.
2. Add Request-based Constraints dan Modify routes.rb
Create a new file inside the /lib folder and give it the name, say subdomain.rb.
Add this code inside that file:
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != "www"
end
end
Then we need to modify a bit our routes.rb by adding constraints
to every URLs that we will use under the subdomain. For an example, pages_controller.rb needs to be accessible under a specific subdomain, so we can write our route like this:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
constraints(Subdomain) do
get '/', to: 'pages#index'
end
end
3. Add Authorization to Controller
We will then need to add before_action :require_subdomain
since the controllers need to be strictly accessible from the subdomain only. This is achievable by modifying our application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :require_subdomain
private
def require_subdomain
not_found unless subdomain_exist?
end
def subdomain_exist?
account = Account.find_by_subdomain(request.subdomain)
!!account
end
def not_found
raise ActionController::RoutingError.new('404')
end
end
In the application_controller.rb above we now have a helper method called require_subdomain
, which is reusable to each controller that requires to be authorized by the subdomain.
class PagesController < ApplicationController
before_action :require_subdomain
def index
# controller logic goes here
end
end
4. Try It Out
Let's use rails console to create a dummy for our Account
or subdomain.
$ rails c
Running via Spring preloader in process 15151
Loading development environment (Rails 6.0.0)
2.6.5 :001 > Account.new(subdomain: 'test').save
(0.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
(0.3ms) BEGIN
Account Exists (0.7ms) SELECT 1 AS one FROM `accounts` WHERE `accounts`.`subdomain` = BINARY 'test' LIMIT 1
SQL (0.5ms) INSERT INTO `accounts` (`subdomain`, `created_at`, `updated_at`) VALUES ('test', '2017-07-22 14:58:35', '2017-07-22 14:58:35')
(35.5ms) COMMIT
=> true
Open the browser then visit test.lvh.me:3000 and also try accessing some random subdomain, for example, doesnotexist.lvh.me:3000.
About lvh.me domain, you can check this article out
If the subdomain is registered in Account
Subdomain doesn't exist in Account
Tip:
We can add some extra validation layers in the Account
creation so we can preserve some subdomain names so your user cannot claim those (i.e. www, admin, app, etc).
Thank you!