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} 但似乎没有任何区别。
你的 webpack 和 package.json 配置绝对没问题。您的特定 MIME 类型(“text/html”)问题在您的 Django 配置中。
content_typeHttpResponse 中的参数。您已将 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 前端的必要步骤:--------预部署- ------------
pipenv shell或 或 venv https://docs.python.org/3/tutorial/venv.html设置虚拟环境/your-venv-directory
__pycache__
db.sqlite3
your-app-name/static/
frontend/dist
.env
Run Code Online (Sandbox Code Playgroud)
django-heroku并添加
import django_heroku
到 settings.py 文件的顶部和 settings.py 文件
django_heroku.settings(locals())
的底部whitenoise并将其作为中间件添加到 settings.pypsycopg2 http://initd.org/psycopg/docs/
这是出了名的难以安装,但是psycopg2-binary像您在这里所做的那样安装原始文件似乎在紧要关头工作。requirements.txt文件并运行pip freeze > requirements.txt并创建一个runtime.txt文件并指定您当前的 python 构建版本,这样事情就不会中断。当您使用生成 Pipfile.lock 的 pipenv 时,我不确定这是绝对必要的,因为 pipenv 取代了但您应该将当前包和 python 构建版本冻结到您的 Pipfile 中,这样事情就不会中断。gunicorn
https
: //devcenter.heroku.com/articles/python-gunicorn https://docs.gunicorn.org/en/latest/settings.htmlProcfile(确保“P”大写!!!)的文件并添加: release: python manage.py migrate
web: gunicorn gettingstarted.wsgi --preload --log-file -
Run Code Online (Sandbox Code Playgroud)
"scripts": {
"build": "webpack -p" }
Run Code Online (Sandbox Code Playgroud)
yarn build) 并确认您的应用程序在本地运行。--------部署------------------- 从您的终端运行:
heroku loginheroku create --region=eu your-app-nameheroku buildpacks:add heroku/nodejsheroku buildpacks:add heroku/python (确保按该顺序添加构建包!)heroku config:set ALLOWED_HOSTS=your-app-name.herokuapp.comheroku config:set DJANGO_SETTINGS_MODULE=project.settings(如果您添加了部署设置文件以使用 django-environ 作为上面建议的模块化您的设置链接,则使用heroku config:set DJANGO_SETTINGS_MODULE=project.settings.herokuDotPyOrWhateverYouNamedIt)heroku config:set SECRET_KEY=INSERTASECURESECRETKEYPASSWORDheroku config:set WEB_CONCURRENCY=1heroku addons:create heroku-postgresql:hobby-devgit push heroku mastergit push heroku yourbranch:master用您的分支名称替换“yourbranch”。heroku run python manage.py migrateheroku ps:scale web=1heroku openheroku logs --tail
以进行调查| 归档时间: |
|
| 查看次数: |
2126 次 |
| 最近记录: |