我在 Chris 的回答中提到,您可以在 server_name 中使用通配符和正则表达式,这些将传递给您的 rails 实例。
server_name *; # handle requests from all domains
在您的 Rails 应用程序中有四种使用方法(据我所知)。
“大包内容”方法
什么都不做
如果你只是让 Nginx 将所有域发送到同一个 rails 应用程序,它们都会得到相同的内容。在这种情况下,www.abc.com 和 www.xyz.com 可以访问相同的数据。这是最容易做到的,因为你什么都不做。
当您想让域具有不同的内容时,此解决方案的局限性就出现了。例如,如果 www.abc.com/about 和 www.xyz.com/about 应该是不同的页面,就会变得很棘手。
“幕后人”法
使用 Nginx 的“重写”
在某些情况下,您可以让 Nginx 将域名重写为子域并将其传递给您的 rails 应用程序。例如:
server_name *; # handle requests from all domains
rewrite ^(?:www.)?([^.]*)\..*$ $1.yourdomain.com last;
这个正则表达式需要一点解释。它只是说从以下任何一个中获取 xyz:www.xyz.com、xyz.com、xzy.co.uk、www.xyz.co.uk。重写将请求从其中任何一个更改为 xyz.yourdomain.com。
这样做的好处是 Nginx 做的非常快,rails 应用程序直到以后才涉及,并且请求内容可以限制在子域中。以Page.where(subdomain: request.subdomain, permalink: params[:permalink]) 为例。
不过,这是相当严格的,因为这意味着子域和域名必须相同。也许这对您的应用程序来说是个问题,也许不是。但是,虽然我在子域中使用了$1,但您可以轻松地将其作为参数插入到 url 中。例如,yourdomain.com/$1 会将请求从 www.xyz.com 重写为 yourdomain.com/xyz。
另一个问题是“幕后的人”。尽管用户访问 www.xyz.com,但重写意味着他们会在地址栏中看到 xyz.yourdomain.com。
“Rails 方式”方法
在 ApplicationController 中使用 Rails 请求对象
您可以使用您的应用程序控制器使用 Rails 的 request 对象来确定与域名关联的内容的范围。
在本例中,我们将使用 ApplicationController 来查找与域名关联的用户帐户。假设您有一个带有 domain_name 属性的 User 模型:
def domain_user
@domain_user ||= User.where(domain_name: request.domain()).take
end
如果对数据库的额外命中造成性能障碍,Redis 可用于缓存这些查找。到目前为止,我的应用程序还没有达到这一点。
此解决方案的弱点在于,虽然查找可能会找到 www.xyz.com,但它会错过 xyz.com。为了适应这种情况,我们可以使用一些正则表达式:
def domain_user
request.domain.match /(?:www.)?(.*)/
@domain_user ||= User.where(domain_name: $1).take
end
这个正则表达式去掉了 www.如果它存在。其余的成为域(ruby 为我们存储在$1 中)。
与 Nginx 重写解决方案不同,如果用户访问 www.xyz.com,他们仍然会在地址栏中看到。
使用路由约束
“对我不起作用”方法
或者,Rails 3 及更高版本具有约束,存在于您的路由文件中的函数可以半动态地生成路由表。我说“半动态”是因为路由表是在应用程序启动时生成的。如果 User 模型(在我们的示例中)发生变化,则需要特别注意重建路由表。对于分布在多个服务器上的应用程序,这可能会变得难以管理,尽管people have done it。
到目前为止,我的所有应用程序最终(如果不是最初)都使用了 ApplicationController 解决方案,因为它被证明是最干净且最容易实现的。从 MCV 的角度来看,这也很有意义。