拒绝从 '.../bundle.js' 执行脚本,因为它的 MIME 类型 ('text/html') 不可执行,并且启用了严格的 MIME 类型检查

sun*_*171 1 heroku reactjs webpack webpack-dev-server

我有一个带有 React/webpack 前端和 Django/python 后端的应用程序。它过去已成功部署到 heroku。自从我在应用程序中做任何事情以来已经有一段时间了,在最近的一些更新之后,我想更新已部署的版本。Heroku 报告应用程序部署成功,但当我尝试访问它时,出现错误:

拒绝执行来自 'https://pairshead-2020.herokuapp.com/bundle.js' 的脚本,因为其 MIME 类型 ('text/html') 不可执行,并且启用了严格的 MIME 类型检查。

这是我的 webpack.config.js:

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/app.js',
  context: path.resolve(__dirname, 'frontend'),
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'frontend/dist'),
    publicPath: '/'
  },
  devtool: 'source-maps',
  module: {
    rules: [
      { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
      { test: /\.css$/, loader: ['style-loader', 'css-loader'] },
      { test: /\.s(a|c)ss$/, loader: ['style-loader', 'css-loader', 'sass-loader'] },
      { test: /\.woff2?$/, loader: 'file-loader' },
      { test: /\.(jpg|png|gif)$/, loader: 'file-loader' }
    ]
  },
  devServer: {
    contentBase: 'src',
    hot: true,
    open: true,
    port: 8000,
    watchContentBase: true,
    historyApiFallback: true,
    proxy: {
      '/api': 'http://localhost:4000'
    }
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: 'src/index.html',
      filename: 'index.html',
      inject: 'body'
    })
  ]
}

Run Code Online (Sandbox Code Playgroud)

这是我的 package.json:

{
  "name": "results",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack -p",
    "serve:backend": "python manage.py runserver 4000",
    "serve:frontend": "webpack-dev-server",
    "seed": "python manage.py loaddata results/fixtures.json"
  },
  "dependencies": {
    "@babel/core": "^7.5.5",
    "@babel/plugin-transform-runtime": "^7.6.0",
    "@babel/preset-env": "^7.6.0",
    "@babel/preset-react": "^7.0.0",
    "@babel/runtime": "^7.6.0",
    "@fortawesome/fontawesome-free": "^5.10.2",
    "axios": "^0.19.0",
    "babel-loader": "^8.0.6",
    "bulma": "^0.7.5",
    "copy-webpack-plugin": "^5.0.4",
    "css-loader": "^3.2.0",
    "file-loader": "^4.2.0",
    "html-webpack-plugin": "^3.2.0",
    "jsonwebtoken": "^8.5.1",
    "moment": "^2.24.0",
    "moment-duration-format": "^2.3.2",
    "node-sass": "^4.12.0",
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-image": "^2.2.0",
    "react-router-dom": "^5.0.1",
    "react-select": "^3.0.4",
    "react-select-async-paginate": "^0.3.13",
    "react-toastify": "^5.4.0",
    "sass-loader": "^7.3.1",
    "style-loader": "^1.0.0",
    "webpack": "^4.39.2",
    "webpack-cli": "^3.3.7"
  },
  "devDependencies": {
    "babel-eslint": "^10.0.3",
    "eslint": "^6.3.0",
    "eslint-plugin-react": "^7.14.3",
    "webpack-dev-server": "^3.8.0"
  },
  "engines": {
    "node": "12.11.1",
    "python": "3.7.5"
  }
}
Run Code Online (Sandbox Code Playgroud)

设置.py

"""
Django settings for project project.

Generated by 'django-admin startproject' using Django 2.2.5.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# import dj_database_url
from dotenv import load_dotenv
import django_heroku
load_dotenv()
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/


SECRET_KEY = os.environ['SECRET_KEY']


# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['0.0.0.0', 'localhost', 'pairshead-results.herokuapp.com']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'rest_framework',
    'results',
    'django_filters',
    'computed_property',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3030',
]
CORS_ORIGIN_REGEX_WHITELIST = [
    'http://localhost:3030',
]

ROOT_URLCONF = 'project.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'project.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-gb'

TIME_ZONE = 'Europe/London'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 25,
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
    'PAGE_SIZE_QUERY_PARAM': 'page_size',
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication'
    ],
}

django_heroku.settings(locals())
# DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)

Run Code Online (Sandbox Code Playgroud)

urls.py(来自项目文件夹)

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('django-admin/', admin.site.urls),
    path('auth/', include('rest_framework.urls')),
    path('api/', include('results.urls')),
    path('api/', include('jwt_auth.urls')),
    path('', include('frontend.urls')),
]

Run Code Online (Sandbox Code Playgroud)

wsgi.py

"""
WSGI config for project project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = get_wsgi_application()

Run Code Online (Sandbox Code Playgroud)

网址.py

from django.urls import path
from .views import clubs
from .views import events
from .views import bands
from .views import crews
from .views import competitors
from .views import times
from .views import results
from .views import masters_adjustments
from .views import original_event_category


urlpatterns = [
path('clubs/', clubs.ClubListView.as_view()),
path('club-data-import/', clubs.ClubDataImport.as_view()),
path('events/', events.EventListView.as_view()),
path('event-data-import/', events.EventDataImport.as_view()),
path('band-data-import/', bands.BandDataImport.as_view()),
path('bands/', bands.BandListView.as_view()),
path('crews/', crews.CrewListView.as_view(), name='crews-list'),
path('crews/<int:pk>', crews.CrewDetailView.as_view(), name='crews-detail'),
path('', crews.CrewListView.as_view()),
path('results/', results.ResultsListView.as_view()),
path('results-export/', results.ResultDataExport.as_view()),
path('crew-update-rankings/', crews.CrewUpdateRankings.as_view()),
path('crew-data-import/', crews.CrewDataImport.as_view()),
path('crew-data-export/', crews.CrewDataExport.as_view()),
path('competitor-data-export/', competitors.CompetitorDataExport.as_view()),
path('competitor-data-import/', competitors.CompetitorDataImport.as_view()),
path('race-times/', times.RaceTimeListView.as_view()),
path('race-times/<int:pk>', times.RaceTimeDetailView.as_view()),
path('crew-race-times/', times.CrewRaceTimesImport.as_view()),
path('masters-adjustments-import/', masters_adjustments.MastersAdjustmentsImport.as_view()),
path('original-event-import/', original_event_category.OriginalEventCategoryImport.as_view()),
]

Run Code Online (Sandbox Code Playgroud)

我有一个包含 9 个视图文件的视图文件夹: init .py

from .bands import *
from .clubs import *
from .competitors import *
from .crews import *
from .events import *
from .times import *
from .results import *
from .masters_adjustments import *

Run Code Online (Sandbox Code Playgroud)

进程文件

web: python manage.py runserver 0.0.0.0:$PORT --noreload

Run Code Online (Sandbox Code Playgroud)

非常感谢任何人可以提供的任何帮助。在阅读了一些类似的问题后,我尝试在 webpack.config.js 中设置 {output: publicPath: '/'} 和 {historyApiFallback: true} 但似乎没有任何区别。

mtc*_*ard 5

你的 webpack 和 package.json 配置绝对没问题。您的特定 MIME 类型(“text/html”)问题在您的 Django 配置中。

  1. React 前端中的 views.py 需要content_typeHttpResponse 中的参数。
  2. Heroku 需要知道你的 Webpack 构建文件在哪里。

您已将 Webpack 配置为编译 React 应用程序的生产版本,命名为“bundle.js”,并告诉 Webpack 将其与 index.html 的生产版本一起放在名为“frontend/dist”的文件夹中。用 Django 的话来说,这些是你的“静态”文件。(如果您使用的是 create-react-app 而不是 Webpack,这就是“build”文件夹中的内容。)在您的 settings.py 中,您需要告诉 heroku 在哪里可以找到这些“静态”文件。(解释如下)。但首先,修复你的前端/views.py。

Home(View)告诉 Heroku(或任何服务器)通过发送 index.html 来响应。您的 Webpack 'HTMLWebpackPlugin' 已将脚本注入 index.html,要求您Assets(View):查找 bundle.js(以及存储在服务器上同一文件夹中的任何其他文件),将其打开为 'rb',(代表 '读取二进制文件'),并将其作为 HttpResponse 返回给用户。

您的“拒绝执行脚本... MIME 类型('text/html')”问题源于 Djangocontent_type对 HttpResponse的默认设置,即text/html. https://docs.djangoproject.com/en/3.1/ref/request-response/#id3

通过content_type='application/javascript'Assets(View)像这样的 return 语句中包含一个参数来解决这个问题:

class Assets(View):

    def get(self, _request, filename):
        path = os.path.join(os.path.dirname(__file__), 'dist', filename)

        if os.path.isfile(path):
            with open(path, 'rb') as file:
                return HttpResponse(file.read(), content_type='application/javascript')
        else:
            return HttpResponseNotFound()
Run Code Online (Sandbox Code Playgroud)

排序后,您需要对 settings.py 文件进行一些更改,以帮助 Heroku 的 Django 配置正确地提供这些文件。

顺便说一句,Heroku 的 Django 部署过程建议安装一个中间件包(“Whitenoise”)来压缩和管理您的静态文件,以及一个 wsgi 接口(“Gunicorn”)来帮助 Heroku 与 Django 进行通信。您缺少这些不会导致您的问题,但它们是 Heroku 当前推荐配置的一部分,因此我将它们包含在下面。

首先,在您的 settings.py 中,您需要在允许的主机中包含您的 heroku 应用程序的地址,以及您的端口和本地主机,如下所示:

ALLOWED_HOSTS = ['your-app-name.herokuapp.com/', '127.0.0.1', 'localhost']
Run Code Online (Sandbox Code Playgroud)

然后你需要添加whitenoise中间件(不要忘记pip install它。)确保你在django的安全中间件之后直接列出它:

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
... 
Run Code Online (Sandbox Code Playgroud)

然后,您需要将 Webpack 将资产存储到 DIRS 的路径添加到前端/dist 中:

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'frontend/dist')],
    'APP_DIRS': True,
     ...
Run Code Online (Sandbox Code Playgroud)

最后也是最重要的是,您需要配置静态文件的存储位置。在你的情况下:

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 
"whitenoise.storage.CompressedManifestStaticFilesStorage"
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/dist')
]
Run Code Online (Sandbox Code Playgroud)

STATIC_ROOT 和 STATICFILES_DIRS 都有设置的原因是 STATIC_ROOT 是 Heroku 自动调用命令时收集所有静态文件的地方:manage.py collectstatic在部署期间。

STATICFILES_DIRS 用于包含 collectstatic 需要查找的其他目录。确保您引用了 Webpack 正确存储构建文件的文件夹的路径!对于您的特定目录设置,应该是“frontend/dist”

不要忘记将 DEBUG 设置更改为 False 以进行部署。

最后,您需要 pip install gunicorn 并将 Procfile 更改为如下所示:

release: python manage.py migrate
web: gunicorn project.wsgi --preload --log-file -
Run Code Online (Sandbox Code Playgroud)

您真的应该查看 Heroku 链接:https : //blog.heroku.com/from-project-to-productionized-python因为它会引导您完成 Heroku 和 Heroku 的环境设置。它还指出 Heroku 需要一个 requirements.txt 文件指定您已安装的 python 包的版本和一个 runtime.txt 文件指定您的 Django 版本。但是,由于您使用的是 Pipenv 而不是 venv,我认为您的 Pipfile 使这些变得不必要。

以下是 Heroku 将 Django 部署到 Heroku 的从产品到生产的说明的大纲,其中包含按正确顺序插入的 React + Webpack 前端的必要步骤:--------预部署- ------------

  1. 注册Heroku
  2. 安装Heroku CLI
  3. 使用pipenv shell或 或 venv https://docs.python.org/3/tutorial/venv.html设置虚拟环境
  4. 更新 .gitignore 以包括:
/your-venv-directory      
__pycache__
db.sqlite3         
your-app-name/static/
frontend/dist
.env
Run Code Online (Sandbox Code Playgroud)
  1. [Heroku 建议您使用 settings.py 进行本地开发,并使用 django-environ 包创建第二个文件 (heroku.py) 以导入您的 env 设置以进行部署。他们的方式要容易得多,但是您的 env 配置管理得很好,所以我会在这里跳过。检查moduleize-your- settingsetting-up-code-heroku-py以获得推荐的设置]
  2. 安装django-heroku并添加 import django_heroku 到 settings.py 文件的顶部和 settings.py 文件 django_heroku.settings(locals()) 的底部
  3. 安装whitenoise并将其作为中间件添加到 settings.py
  4. 安装psycopg2 http://initd.org/psycopg/docs/ 这是出了名的难以安装,但是psycopg2-binary像您在这里所做的那样安装原始文件似乎在紧要关头工作。
  5. 创建一个requirements.txt文件并运行pip freeze > requirements.txt并创建一个runtime.txt文件并指定您当前的 python 构建版本,这样事情就不会中断。当您使用生成 Pipfile.lock 的 pipenv 时,我不确定这是绝对必要的,因为 pipenv 取代了但您应该将当前包和 python 构建版本冻结到您的 Pipfile 中,这样事情就不会中断。
  6. 安装实际为您的 heroku 应用程序提供服务的 Web 服务器网关接口 (WSGI) 进程:gunicorn https : //devcenter.heroku.com/articles/python-gunicorn https://docs.gunicorn.org/en/latest/settings.html
  7. 创建一个名为Procfile(确保“P”大写!!!)的文件并添加:
    release: python manage.py migrate
    web: gunicorn gettingstarted.wsgi --preload --log-file -
Run Code Online (Sandbox Code Playgroud)
  1. 确保你的 package.json 中有一个构建脚本
"scripts": {
  "build": "webpack -p" }
Run Code Online (Sandbox Code Playgroud)
  1. 运行此构建脚本 ( yarn build) 并确认您的应用程序在本地运行。

--------部署------------------- 从您的终端运行:

  1. heroku login
  2. heroku create --region=eu your-app-name
  3. heroku buildpacks:add heroku/nodejs
  4. heroku buildpacks:add heroku/python (确保按该顺序添加构建包!)
  5. heroku config:set ALLOWED_HOSTS=your-app-name.herokuapp.com
  6. heroku config:set DJANGO_SETTINGS_MODULE=project.settings(如果您添加了部署设置文件以使用 django-environ 作为上面建议的模块化您的设置链接,则使用heroku config:set DJANGO_SETTINGS_MODULE=project.settings.herokuDotPyOrWhateverYouNamedIt
  7. heroku config:set SECRET_KEY=INSERTASECURESECRETKEYPASSWORD
  8. heroku config:set WEB_CONCURRENCY=1
  9. heroku addons:create heroku-postgresql:hobby-dev
  10. 确保您在主分支上并且所有更改都已提交到 git 并运行, git push heroku master
    或者如果您正在另一个分支上工作,请提交到 git 并运行 git push heroku yourbranch:master用您的分支名称替换“yourbranch”。
  11. heroku run python manage.py migrate
  12. 如果您有一个包含数据库种子的 fixtures.json 文件,请运行 heroku run python manage.py loaddata your-django-project-name/fixtures.json
  13. heroku ps:scale web=1
  14. heroku open
  15. 如果有任何错误,请运行 heroku logs --tail 以进行调查