在子路径中通过nginx+uwsgi运行django应用程序

19
我想在开发服务器的子目录别名中运行一个简单的测试项目。基本设置是使用nginx,并将所有内容传递到子目录中的wsgi应用程序。
很明显Django不理解它在子目录别名中运行,这完全破坏了URL的生成和解析。 我在文档中找不到任何类似前缀的设置,我的谷歌搜索也没有什么帮助......所以我在这里寻求帮助。
我唯一找到的是FORCE_SCRIPT_NAME设置,它至少修复了URL的生成。(见:http://docs.webfaction.com/software/django/config.html#mounting-a-django-application-on-a-subpath) 不幸的是,即使提到的网站建议如此,这并不能修复urlconf解析。
是否可能在子目录别名中运行Django应用程序?如果可以,该怎么做?
nginx配置:
server {
        location /fancyprojectname/static {
                alias /srv/fancyprojectname/static;
        }

        location /fancyprojectname/ {
                uwsgi_pass unix://var/run/uwsgi/app/fancyprojectname/socket;
                include uwsgi_params;
        }
}

编辑

在 Nginx 的 location 中设置 "uwsgi_param SCRIPT_NAME /fancyprojectname;" 可以使 FORCE_SCRIPT_NAME 不必要 - 遗憾的是 URL 匹配仍然无法工作。

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Uncomment the admin/doc line below to enable admin documentation:
    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)
我认为正在发生的事情是:由于管理员正则表达式以 "^admin" 开头,而实际 URL 是 "fancyprojectname/admin/",Django 无法正确匹配 URL,即使 SCRIPT_NAME 已设置。 解决方案 因此,确实存在 SCRIPT_NAME 的问题。 WSGI 规范 如下所述:
SCRIPT_NAME 请求 URL“路径”的初始部分,对应于应用程序对象,以便应用程序知道其虚拟“位置”。如果应用程序对应于服务器的“根”,则可能为空字符串。
PATH_INFO 请求 URL“路径”的剩余部分,指定应用程序中请求目标的虚拟“位置”。如果请求 URL 定位到应用程序根目录并且没有尾部斜杠,则可能为空字符串。
Nginx 不会自动设置 SCRIPT_NAME,因此这需要在任何情况下进行设置。然后 PATH_INFO 是错误的,因为在默认设置中,Nginx 将其设置为 $document_uri,这将是完整的 URL。
"uwsgi_modifier1 30;" 告诉 Nginx 设置 UWSGI_MODIFIER_MANAGE_PATH_INFO,这反过来告诉 UWSGI 去掉 PATH_INFO 的 SCRIPT_NAME。
这些设置的组合似乎有效,因为 Django 现在可以正确生成和匹配 URL。

请查看 https://dev59.com/zlsV5IYBdhLWcg3wyxV_#40496307,您将找到实现此操作的更新方法。 - clapas
请参阅:如何使用uwsgi挂载Django应用程序 - Ross Rogers
FORCE_SCRIPT_NAME示例的链接已经失效了;对于任何想知道那里写了什么的人,这里是Wayback Machine上最后保存的版本。 - Ivan Shatsky
4个回答

8

这是错误的:

Django显然不理解它在子目录别名中运行,这完全破坏了URL的生成和解析。

Django 确实 理解并且处理它。服务器应该自己设置SCRIPT_NAME :你发现自己使用FORCE_SCRIPT_NAME 表明问题出在你的Nginx配置上,而不是Django本身。

我怀疑问题出在location的使用上,而不是一个更合适的指令。不幸的是,我对Nginx/uwsgi不是很了解。在Apache/mod_wsgi中,你会这样做:

WSGIScriptAlias /mysite /usr/local/django/mysite/apache/django.wsgi

为了告诉mod_wsgi站点从mysite开始而不是根目录,需要做出相应的配置。很可能在nginx/uwsgi中也有类似的命令。

3
好的,我在nginx中尝试了“uwsgi_param SCRIPT_NAME /fancyprojectname;”,这似乎“有效”,因为现在我可以在设置中注释掉FORCE_SCRIPT_NAME,URL将正确生成 - 但这仍然无法解决URL匹配问题。我将在问题中附加当前的URL配置。 - Strayer
5
看起来现在它正在工作-SCRIPT_NAME不够用,但设置“uwsgi_modifier1 30;”就可以了。我不知道这个设置是干什么的,但当我了解之后,我会再次更新问题并接受这个有效答案。感谢你的指引! - Strayer
我必须补充说明的是,“uwsgi_modifier1 30”只是答案的一半。对我有效的方法是在我的uwsgi.ini文件中添加“env = DJANGO_SETTINGS_MODULE=my_app.settings”。 - Lukasz Dynowski

7
这种方法(在2016年有效)现已过时。如果您正在运行LTS软件,则仍然适用。

“注意:古老的uWSGI版本曾支持所谓的“uwsgi_modifier1 30”方法。不要这样做。这是一个非常丑陋的黑客方法”

请参见在同一进程中托管多个应用程序(又称管理SCRIPT_NAME和PATH_INFO)

原始答案

如果在http://www.example.com/(您的基本域)托管Django站点时,此Nginx位置块有效:

location / {
    uwsgi_pass unix:/tmp/fancyprojectname.socket;
    include /etc/nginx/uwsgi_params;
}

那么这将在您的基本域名下的子路径 http://www.example.com/subpath/ 上起作用:

location /subpath {
    uwsgi_pass unix:/tmp/fancyprojectname.socket;
    uwsgi_param SCRIPT_NAME /subpath; # explicitly set SCRIPT_NAME to match subpath
    uwsgi_modifier1 30; # strips SCRIPT_NAME from PATH_INFO (the url passed to Django)
    include /etc/nginx/uwsgi_params;
}

...而且无需在Django设置中设置FORCE_SCRIPT_NAME。

参考资料:


谢谢,现在我已经成功在nginx的子路径下运行Hatta wiki - David Cary
不要忘记在您的uwsgi.ini文件中添加“env = DJANGO_SETTINGS_MODULE=your_app.settings”。 - Lukasz Dynowski

7
现在最新版本的Nginx和uWSGI已经删除了uwsgi_modifier1 30(我不想使用一些hacky rewrite规则),因此我必须找到一种更新的方法来使它正常工作: uWSGI配置:
[uwsgi]
# Requires PCRE support compiled into uWSGI
route-run = fixpathinfo:

Nginx配置:

server {
    location /fancyprojectname/static {
        alias /srv/fancyprojectname/static;
    }

    location /fancyprojectname {
        uwsgi_pass unix://var/run/uwsgi/app/fancyprojectname/socket;
        uwsgi_param SCRIPT_NAME /fancyprojectname; # Pass the URL prefix to uWSGI so the "fixpathinfo:" route-rule can strip it out
        include uwsgi_params;
    }
}

如果以上方法没有解决问题:尝试安装libpcre和libpcre-dev,然后使用pip install -I --no-cache-dir uwsgi重新安装uwsgi。uWSGI的内部路由子系统需要在编译/安装uWSGI之前安装PCRE库。有关uWSGI和PCRE的更多信息。


1
这是最终解决方案。 - Appost
1
这个解决方案对于 Django 3+ 来说是最好的,至少对我来说是最好的。只需要修改所有的绝对路径,并将它们替换为 URL/反转过滤器即可。 - Mitchell Walls

0

可以通过以下技巧,在包含uwsgi_params文件之前重写$uri nginx内部变量,从而使SCRIPT_NAMEPATH_INFO两个uWSGI变量都能够被nginx正确填充,而无需设置modifier1头数据包(已弃用且在python3中不再起作用)或依赖于uWSGI服务器中的PCRE支持使用route-run参数。

location /app_prefix/ {
    rewrite ^/app_prefix(.*) $1 break;
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /app_prefix;
    uwsgi_pass unix:/path/to/socket;
}

另一种解决方案是使用manage-script-name uWSGI服务器参数(命令行示例, 配置文件示例)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接