【问题标题】:Fail to serve static files in deployed Django app (Docker, gunicorn, whitenoise, Heroku)无法在已部署的 Django 应用程序(Docker、gunicorn、whitenoise、Heroku)中提供静态文件
【发布时间】:2021-11-25 10:12:42
【问题描述】:

我已经构建了一个仅提供 JSON 响应的 Django REST API。我的问题是,在生产中(使用 debug=False 部署在 Heroku 上),该应用程序似乎没有提供适当的管理界面样式所需的相关静态文件(仅用于静态文件的用例)。请注意,在开发中(带有 debug=True 的 localhost),管理界面的样式正确。

转到已部署 (Heroku) 地址的管理路由,内容已交付,但没有任何样式。浏览器开发人员工具指示由于 500 错误代码而无法加载样式表。 Django 日志输出显示以下详细信息。

django.request ERROR    Internal Server Error: /static/admin/css/base.1f418065fc2c.css
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/app/secure_my_spot/custom_middleware/request_logging.py", line 30, in __call__
print(f"Content: {response.content}")
File "/usr/local/lib/python3.8/site-packages/django/http/response.py", line 407, in content
raise AttributeError(
AttributeError: This WhiteNoiseFileResponse instance has no `content` attribute. Use `streaming_content` instead.

我已经进入 Heroku dyno 并验证了导致 500 错误的静态文件实际上根据 Django 的 settings.py 位于 static_root 中。我一直在花费大量时间在互联网上搜索线索,以寻找可能导致文件无法在生产中提供的线索,但无论我尝试过什么,它都没有奏效。

以下是相关文件和设置的精选摘要。

Dockerfile

FROM python:3.8-alpine

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN apk update && apk add \
    gcc \
    libc-dev \
    python3-dev \
    musl-dev \
    postgresql-dev \
    vim \
    bash --no-cache --upgrade

RUN pip install \
    pipenv \
    psycopg2

COPY Pipfile Pipfile.lock ./

RUN pipenv install --system --deploy --pre

COPY . .

RUN python manage.py collectstatic --noinput

CMD gunicorn secure_my_spot.wsgi:application --bind 0.0.0.0:$PORT

heroku.yml

build:
  docker:
    web: Dockerfile
release:
  image: web
  command:
    - chmod u+x heroku_entrypoint.sh
run:
  web: ./heroku_entrypoint.sh

heroku_entrypoint.sh

#!/bin/bash

python manage.py collectstatic --noinput
python manage.py migrate --noinput
gunicorn secure_my_spot.wsgi:application --bind 0.0.0.0:$PORT

settings.py

BASE_DIR = Path(__file__).resolve().parent.parent

INSTALLED_APPS = [
    "django_extensions",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "rest_framework.authtoken",
    "app",
    "corsheaders",
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "secure_my_spot.custom_middleware.request_logging.RequestLogging",
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

STATIC_URL = "/static/"

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

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

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

wsgi.py

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "secure_my_spot.settings")

if settings.DEBUG:
    application = StaticFilesHandler(get_wsgi_application())
else:
    application = get_wsgi_application()

【问题讨论】:

    标签: django docker heroku gunicorn whitenoise


    【解决方案1】:

    阅读this 后,我将 Django SecurityMiddleware 移至中间件数组的顶部,紧随其后的是 WhiteNoiseMiddleware(见下文)。这个小改动起到了作用,所有静态资产现在都按预期提供。

    settings.py

    MIDDLEWARE = [
        "django.middleware.security.SecurityMiddleware",
        "whitenoise.middleware.WhiteNoiseMiddleware",
        "corsheaders.middleware.CorsMiddleware",
        "secure_my_spot.custom_middleware.request_logging.RequestLogging",
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.csrf.CsrfViewMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
    ]
    

    不幸的是,我没有很好的解释为什么仅仅改变中间件数组元素的顺序会导致这个问题。也许将 WhiteNoiseMiddleware 移到 CorsMiddleware 之前很重要,即使错误消息没有表明任何 CORS 错误。

    【讨论】:

      猜你喜欢
      • 2020-09-17
      • 2019-09-19
      • 2015-02-05
      • 2021-06-03
      • 2023-03-05
      • 2020-12-17
      • 2013-07-03
      • 1970-01-01
      • 2020-07-22
      相关资源
      最近更新 更多