reactjs - 拒绝执行来自 '.../bundle.js' 的脚本,因为它的 MIME 类型 ('text/html') 不可执行,并且启用了严格的 MIME 类型检查
问题描述
我有一个带有 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'
})
]
}
这是我的 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"
}
}
设置.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)
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')),
]
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()
网址.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()),
]
我有一个包含 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 *
过程文件
web: python manage.py runserver 0.0.0.0:$PORT --noreload
非常感谢任何人可以提供的任何帮助。在阅读了一些类似的问题后,我尝试在 webpack.config.js 中设置 {output: publicPath: '/'} 和 {historyApiFallback: true} 但似乎没有任何区别。
解决方案
您的 webpack 和 package.json 配置绝对没问题。您的特定 MIME 类型(“text/html”)问题存在于您的 Django 配置中。
- React 前端中的 views.py 需要
content_type
在 HttpResponse 中添加一个参数。 - 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',(代表 ' read binary'),并将其作为 HttpResponse 返回给用户。
您的“拒绝执行脚本 ... MIME 类型('text/html')”问题源于 Djangocontent_type
对 HttpResponse 的默认设置,即text/html
. https://docs.djangoproject.com/en/3.1/ref/request-response/#id3
content_type='application/javascript'
通过在 return 语句中包含一个参数来解决这个问题,Assets(View)
如下所示:
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()
排序后,您需要对 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']
然后您需要添加 whitenoise 中间件(不要忘记 pip 安装它。)确保将它列在第二个,直接在 django 的安全中间件之后:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
...
然后,您需要添加 Webpack 将资产存储到 DIRS 的路径,在您的情况下为 frontend/dist:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'frontend/dist')],
'APP_DIRS': True,
...
最后也是最重要的,您需要配置存储静态文件的位置。在你的情况下:
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')
]
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 -
您真的应该查看 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 前端的必要步骤:--------部署前- ------------
- 注册Heroku
- 安装Heroku CLI
pipenv shell
使用or 或 venv https://docs.python.org/3/tutorial/venv.html设置虚拟环境- 更新 .gitignore 以包括:
/your-venv-directory
__pycache__
db.sqlite3
your-app-name/static/
frontend/dist
.env
- [Heroku 建议您使用 settings.py 进行本地开发,并使用包 django-environ 创建第二个文件 (heroku.py) 以导入您的环境设置以进行部署。他们的方式要容易得多,但你的环境配置管理得很好,所以我会在这里跳过。检查modulesize-your-settings和setting-up-code-heroku-py以获得推荐的设置]
- 安装
django-heroku
并添加import django_heroku
到 settings.py 文件的顶部和 settings.py 文件django_heroku.settings(locals())
的底部 - 安装
whitenoise
并将其作为中间件添加到 settings.py - 安装
psycopg2
http://initd.org/psycopg/docs/ 这是出了名的难安装,但是psycopg2-binary
像你在这里做的那样安装 raw 似乎在紧要关头。 - 创建一个
requirements.txt
文件并运行pip freeze > requirements.txt
并创建一个runtime.txt
文件并指定您当前的 python 构建版本,这样事情就不会中断。当您使用生成 Pipfile.lock 的 pipenv 时,我不确定这是绝对必要的,因为 pipenv 会取代但您应该将当前包和 python 构建版本冻结到您的 Pipfile 中,这样事情就不会中断。 - 安装实际服务于您的 heroku 应用程序的 Web 服务器网关接口 (WSGI) 进程:
gunicorn
https ://devcenter.heroku.com/articles/python-gunicorn https://docs.gunicorn.org/en/latest/settings.html - 创建一个名为
Procfile
(确保“P”大写!!!)的文件并添加:
release: python manage.py migrate
web: gunicorn gettingstarted.wsgi --preload --log-file -
- 确保你的 package.json 中有一个构建脚本
"scripts": {
"build": "webpack -p" }
- 运行此构建脚本 (
yarn build
) 并确认您的应用在本地运行。
--------部署------- 从您的终端运行:
heroku login
heroku create --region=eu your-app-name
heroku buildpacks:add heroku/nodejs
heroku buildpacks:add heroku/python
(确保按该顺序添加构建包!)heroku config:set ALLOWED_HOSTS=your-app-name.herokuapp.com
heroku config:set DJANGO_SETTINGS_MODULE=project.settings
(如果您添加了部署设置文件以使用 django-environ 作为上面建议的模块化设置链接,则使用heroku config:set DJANGO_SETTINGS_MODULE=project.settings.herokuDotPyOrWhateverYouNamedIt
)heroku config:set SECRET_KEY=INSERTASECURESECRETKEYPASSWORD
heroku config:set WEB_CONCURRENCY=1
heroku addons:create heroku-postgresql:hobby-dev
- 确保您在您的主分支上,并且所有更改都已提交到 git 并运行
git push heroku master
,或者如果您在另一个分支上工作,请提交到 git 并运行git push heroku yourbranch:master
用您的分支名称替换“yourbranch”。 heroku run python manage.py migrate
- 如果您有一个包含数据库种子的 fixtures.json 文件,请运行 heroku run python manage.py loaddata your-django-project-name/fixtures.json
heroku ps:scale web=1
heroku open
- 如果有任何错误,请运行
heroku logs --tail
调查
推荐阅读
- node.js - pool.request 不是函数
- android - 存在 viewpager 性能问题的导航组件
- python - 在一系列任务结束后运行一些清理任务的最佳方法是什么
- r - 在 Rstudio 中,read.xlsx 函数创建了更多用 'NA's 填充的列,它不应该
- excel - 运行时错误 5981,应用程序定义或对象定义错误
- java - 在使用 OHS 的 Weblogic 12.2.1 上的 HTTP 基本身份验证中两次询问用户名和密码
- excel - 过滤单元格中的非零值并在单独的列/工作表中复制大数据的值
- c++ - 如何避免在 Windows 10 上崩溃后重新启动应用程序
- python - 根据特定条件重命名键
- reporting-services - SSRS 报告返回错误 - 组“grp”的排序表达式包含错误:尝试除以零