Django、子域名与mod_rewrite。部署时URL混乱问题

2 投票
1 回答
1442 浏览
提问于 2025-04-16 02:39

我有一个Django应用程序,运行在比如说 example.com 上。这个应用里有几个子应用,比如 strengthspeedskill。它们的访问地址分别是 http://example.com/strengthhttp://example.com/speedhttp://example.com/skill。在开发的时候,我用 runserver 启动服务器,一切都很顺利。

但是在部署的时候,我需要设置子域名来对应这些子应用。具体来说,我想让 http://x.example.com 对应到 http://example.com/x(这里的 x 是上面提到的值),然后就可以继续处理请求了。

我查了一下,发现有两种方法可以做到这一点。

  • 一种是使用中间件来获取URL中的子域名部分,然后把它保存在传给视图方法的 request 对象里。接着我可以在应用逻辑中处理这些信息。
  • 另一种是使用Apache的 mod_rewrite 来进行URL的转换,然后让我的应用正常运行。

我选择了后者,因为看起来更整洁,而且我觉得这样就不需要在核心应用中加入与部署相关的代码。

不过,现在我遇到了一个问题,找不到解决办法。在 skill 应用里,我有一个命名的URL叫 skill_home,它的地址是 http://example.com/skill。但是一旦部署后,skill_home 的URL变成了 http://skill.example.com/skill。Django把 /skill 加到了顶级域名后面,这就是我得到的结果。如果我访问这个URL,mod_rewrite 会把它改成 http://skill.example.com/skill/skill,这样就不行了。

我的 mod_rewrite 配置大致是这样的:

RewriteCond %{HTTP_HOST} !www.example.com$ [NC]
RewriteCond %{HTTP_HOST} ^(www.)?skill.example.com [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) /skill/$1 

我该怎么优雅地解决这个问题呢?

1 个回答

3

在这个回答中,我假设你愿意为每个子域名做一个 mod_rewrite 的设置。我觉得这可能不适用于任何子域名(也就是你提到的 x)。

这个设置会去掉网址开头的 /skill/,这样你的应用就能继续正常工作:

RewriteCond %{HTTP_HOST} !www.example.com$ [NC]
RewriteCond %{HTTP_HOST} ^(www.)?skill.example.com [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (/skill/)?(.*) /skill/$2

更新

好的,你想要在链接中去掉网址的开头部分。

基本上,这意味着你需要写一个自定义标签来替代 {% url %} 标签,类似这样:

import re
from django.template import Library
from django.template.defaulttags import URLNode, url

register = Library()

class SubdomainURLNode(URLNode):
    def render(self, context):
        domain = context['request'].get_host()
        subdomain = re.sub(r'^www\.','',domain).split('.')[0]
        path = super(SubdomainURLNode, self).render(context)
        return re.sub(r'^/%s/' % subdomain, '/', path)

@register.tag
def subdomainurl(parser, token, node_cls=SubdomainURLNode):
    """Just like {% url %} but checks for a subdomain."""
    node_instance = url(parser, token)
    return node_cls(view_name=node_instance.view_name,
        args=node_instance.args,
        kwargs=node_instance.kwargs,
        asvar=node_instance.asvar)

我在我的服务器上测试过,这个方法似乎有效。

撰写回答