【问题标题】:How to do URL rewrite for folder structure如何对文件夹结构进行 URL 重写
【发布时间】:2019-09-28 19:05:05
【问题描述】:

我有我的 Django 应用程序,我有以下用例:

  1. 本地开发。由于我的应用程序大多很简单,我更喜欢通常的manage.py runserver 方法。因此,我希望我的应用适用于这种情况。
  2. 生产环境。我们在不同的 URL 下拥有一个 Web 服务器和主机工具,例如 https://myserver.com/tool1https://myserver.com/tool2 等。所以我想把我的应用程序放在这个结构中
  3. (理论上)除了用例 2,我可能希望将应用程序托管在自己的域下,例如 https://tool.myserver.com

但是当我尝试这样做时,我遇到了静态文件的问题(叹气!),因为如果我有 STATIC_URL 作为相对路径 STATIC_URL='static/' 那么它不适用于“嵌套”页面(即,如果我在 myserver.com/tool1/page 然后静态 URL 将映射到 myserver.com/tool1/page/static 这是不正确的)。 另一方面,如果我使用绝对路径 STATIC_URL='/static/' 那么它根本不适用于案例 2,因为 Django 应用程序对它所在的 URL 的 /tool1 部分一无所知。

我可以为STATIC_URL 使用两种不同的变体,具体取决于环境和硬编码STATIC_URL='/tool1/static/',但是相同的代码不适用于案例3...

我应该如何处理这种情况?

更新

实际上,我意识到这更像是一般的 nginx+后端问题,而不是 Django 问题。因为在一天结束时后端生成的网页很可能会有src="/static/...."(除非我在后端添加一些黑客以插入前缀/tool1)。我想知道它通常是如何处理的?有一种方法可以替换nginx中的实际HTML内容,但是确实会影响性能...

UPD2

似乎很多人误读了我的问题,认为唯一的问题在于静态文件。然而这只是一个例子,因为正如 Ivan 正确提到的,链接也存在同样的问题。通常在页面上我有a href="/category/post?id=1" 之类的链接。显然,当站点以my.domain/tool1 打开时,这将解析为my.domain/category...,这是错误的(我希望它指向my.domain/tool1/category...。)

【问题讨论】:

  • 你是如何解决所有其他页面/网址的?
  • @Ivan,你什么意思?
  • 您的网络应用程序中有超链接吗?你如何从另一个页面引用一个页面?
  • 由于我的应用程序很简单,我并没有真正的多级分层链接,我有一个对同一页面的请求(如 href="#" 工作正常)。但是,是的,你是对的,同样的问题我也会有链接,不仅仅是静态
  • STATIC_URL='static/' 设置在哪里?你能显示你的配置文件或相关部分吗?

标签: python django nginx url-rewriting


【解决方案1】:

处理来自 django 应用程序的 STATIC_URL 可能会导致开销。由于您有多个子目录,为什么不只从其中一个提供静态文件呢?提供静态文件时无需匹配子目录。

比方说,如果你这样设置STATIC_URL

STATIC_URL = "/tool1/static/"

然后你可以像这样配置NGINX

server {
    listen 80

    server_name *.myserver.com;

    location /tool1/static/ {
        root /path/to/STATIC_ROOT;
    }

    location / {
        proxy_pass http://localhost:8000;  # <-- No trailing slash to make it work with gunicorn
    }

    location /tool2/ {
        proxy_pass http://localhost:8000;
        proxy_set_header SCRIPT_NAME /tool2;  # <-- For serving in sub path
    }

    location /tool1/ {
        proxy_pass http://localhost:8000;
        proxy_set_header SCRIPT_NAME /tool1; # <-- For serving in sub path
    }

}

并在设置中将 STATIC_URL 更新为 /tool1/static/。还要确保您的STATIC_ROOT 指向静态文件将驻留在服务器中的正确路径。最后,在部署静态文件之前,需要先运行collectstatic command将静态文件放到STATIC_ROOT目录下,然后重启NGINX服务器。

更新:不建议使用硬编码网址。相反,在模板中,您应该使用{% URL '&lt;url_name&gt; '%} 标记,或在python 代码中使用reverse()。这样,django 将自行解析 url。当它在/tool1 位置时,url 将被解析为/tool1/url_name/,当你在/tool2 位置时,url 将被解析为/tool2/url_name(基于SCRIPT_NAME)。

【讨论】:

  • 我是否理解正确,您建议在 Django 中硬编码 tool1 一次,在 Nginx 中硬编码两次?这样做有什么好处,而不是显式地将它放在 nginx 端一次,然后在支持端动态使用SCRIPT_NAME?另外,这对于页面上的链接也没有帮助,例如a href="/main/page?id=1" - 当应用程序位于文件夹中时,这无法正确解决。
  • 在调用 urls 时,你不应该硬编码它。而是使用 {% url %} 标签,所以 django 会自己解决它。另外,您将如何在后端动态定义SCRIPT_NAME?在 django 中有一个名为 FORCE_SCRIPT_NAME 的变量,但它不是动态的。但是你不需要那个,因为你可以在 NGINX 配置中明确地把它放在头 SCRIPT_NAME 中
  • 来自url 文档:“返回绝对路径引用(没有域名的 URL)”。所以它会给我返回完全相同的网址href="/my/page"
  • 可能不会。由于 SCRIPT_NAME,它应该配置像 '/tool1/url/' 这样的 url,如果它在位置 /tool1
  • 听起来像无证魔法,哇。但是感谢您指出这一点,如果转发/设置正确,Django 确实能够自动处理 SCRIPT_NAME 标头。我今天学到了一些新东西:)
【解决方案2】:

我建议将您的静态文件放在 AWS S3、Google Cloud Storage 等存储服务中。 这样,它始终可用于您拥有的任何 URL(嵌套、子域等)

示例设置(可能需要更多步骤):

  • 更改您的settings.py

    STATIC_URL = 'https://storage.googleapis.com/your_bucket/static/'

    STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

    STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)

  • 在部署您的应用之前,请运行 manage.py collectstatic --noinput

  • staticfiles 文件夹中的文件复制到存储服务并确保它们是公开的
  • 部署您的应用

P.S 我发现 whitenoise 确实有助于静态文件大小和更轻松的部署

【讨论】:

  • 在这里使用云并没有帮助,因为问题不仅与静态文件有关(如评论中所述),而且与一般链接有关。每个 CDN 都有一个注释“不要将 CDN 用作主存储,仅将其用作镜像”,所以我仍然必须将我的文件存储在某个地方(包括在我修改静态时用于本地开发)
  • 该解决方案适用于本地(文件从本地文件夹加载)、开发和生产环境。存储在存储服务中的好处是多区域(如果您愿意),提高网页加载速度并减少应用服务器上的负载。至于其他链接,我不明白为什么它们不起作用。你能解释一下吗?
  • 查看 Ivan 对问题本身的评论。因为我有链接 `a href='/my/folder/page?id=1' 并且当应用程序位于文件夹中时无法正确解析
【解决方案3】:

似乎没有真正好的方法来解决它。但是我发现了几个选项:

始终使用一个规范地址(个人首选)

我应该问自己“为什么您希望您的工具在 https://myserver.com/tool1https://tool1.myserver.com 都可用?”。实际上,我没有任何理由为什么要同时单独提供它。所以只需选择“规范”地址并设置正确的重定向:

如果域是规范的:

    server_name myserver.com
    location /tool1/ {
            rewrite ^/tool1/?(.*)$ https://tool1.myserver.com/$1 permanent;
    }

如果文件夹是规范的:

    server_name tool1.myserver.com
    location / {
            rewrite ^ https://myserver.com/tool1$request_uri? permanent;
    }

然后在后端使用与规范地址相对应的 STATIC_URL 的任何 one 选项(即/static 用于子域或/tool1/static 用于文件夹)

其他标题或 URL 参数

如果有理由(告诉我为什么!)不使用重定向并且同时使用两个地址,那么正如 ruddra 所提到的,可以添加额外的标头,然后在后端使用它们以生成稍微不同的页面:

location /tool1/ {
    <proxy_pass something>
    proxy_set_header SCRIPT_NAME /tool1;
}

如果您选择文件夹作为规范地址,则可以使用相同的方法,那么您可能希望避免在后端硬编码tool1。然后,也许您可​​以使用自定义标头或附加 url 参数,如 ?from=domain [1] 可以在后端处理。在这里,我没有现成的解决方案,因为我更喜欢子域案例。

【讨论】:

  • 假设您有 2 个不同的域 abc.com/sales/ 和 xyz.com/e-commerce/,并且您想为这两个域提供相同的应用程序,然后是 @987654331 @ 方法很有用。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-05
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 1970-01-01
  • 2012-05-23
相关资源
最近更新 更多