Почему я получаю ошибку типа MIME в приложении Django/React при загрузке css/js из Aws?
Итак, я развернул Django-Rest/React приложение на Heroku, где я обслуживаю свои статические и медиа файлы на AWS S3 buckets. После размещения на Heroku и доступа к URL API или URL администратора все работает нормально, но когда я пытаюсь получить доступ к моим URL React, я получаю ошибку типа MIME.
На вкладке сеть в инструментах разработчика я получаю статус 301 на моих JS и CSS файлах.
В консоли я получаю:
Refused to apply style from 'https://app.herokuapp.com/static/css/main.9d3ee958.css/' because its MIME type
('text/html') is not a supported stylesheet MIME type, and strict MIME checking is
enabled.
app.herokuapp.com/:1 Refused to apply style from 'https://app.herokuapp.com/static/css/main.9d3ee958.css/' because its MIME type
('text/html') is not a supported stylesheet MIME type, and strict MIME checking is
enabled.
app.herokuapp.com/:1 Refused to execute script from 'https://app.herokuapp.com/static/js/main.3b833115.js/' because its MIME type
('text/html') is not executable, and strict MIME type checking is enabled.
Хотя URL выше правильный и у меня действительно есть эти файлы в моем ведре.
Вот мои производственные настройки:
from decouple import config
import django_heroku
import dj_database_url
from .base import *
SECRET_KEY = config('SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = ['*']
ROOT_URLCONF = 'portfolio.urls_prod'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'frontend/build')
],
'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',
],
},
},
]
# DATABASE
DATABASES = {}
DATABASES['default'] = dj_database_url.config(conn_max_age=600)
# HEROKU
django_heroku.settings(locals())
# AWS S3 SETTINGS
AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_DEFAULT_ACL = 'public-read'
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_HEADERS = {
'Access-Control-Allow-Origin': '*',
}
AWS_QUERYSTRING_AUTH = False
# AWS STATIC SETTINGS
AWS_LOCATION = 'static'
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'portfolio.storage_backend.StaticStorage'
# AWS MEDIA SETTINGS
DEFAULT_FILE_STORAGE = 'portfolio.storage_backend.MediaStorage'
MEDIA_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, 'media')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend/build/static'),
]
# HEROKU LOGGING
DEBUG_PROPAGATE_EXCEPTIONS = True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt' : "%d/%b/%Y %H:%M:%S"
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'MYAPP': {
'handlers': ['console'],
'level': 'DEBUG',
},
}
}
# HTTPS SETTING
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
# HSTS SETTINGS
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
options = DATABASES['default'].get('OPTIONS', {})
options.pop('sslmode', None)
Вот мой код storage_backend.py:
from storages.backends.s3boto3 import S3Boto3Storage
class MediaStorage(S3Boto3Storage):
location = 'media'
file_overwrite = False
class StaticStorage(S3Boto3Storage):
location = 'static'
default_acl = 'public-read'
Вот мой код url_prod.py:
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.generic import TemplateView
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path('api/', include('api.urls')),
re_path(r'^(?P<path>.*)/$', TemplateView.as_view(template_name='index.html')),
path('', TemplateView.as_view(template_name='index.html')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Такова моя политика в отношении ведра:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}
Поэтому я решил ответить на свой собственный вопрос на случай, если кто-то увидит это и столкнется с той же проблемой.
Проблема заключалась в том, что каждый раз, когда я запускал "npm run build", React создавал мой index.html с файлами js и CSS, связанными с моим локальным путем в папке build.
После долгих исследований я решил создать файл .env в корневом каталоге моего приложения react с такими настройками:
PUBLIC_URL=https://path-to-bucket.s3.amazonaws.com
Теперь каждый раз, когда я запускаю сборку, react будет добавлять этот URL в "src" в моих js и CSS файлах. Проверьте документацию здесь.
Перед добавлением этой настройки:
<html lang="en">
<head>
<script defer="defer" src="../static/js/main.d91dacb2.js"></script>
<link href="../static/css/main.9d3ee958.css" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
</body>
После добавления этой настройки:
<html lang="en">
<head>
<script defer="defer" src="https://path-to-bucket.s3.amazonaws.com/static/js/main.d91dacb2.js"></script>
<link href="https://path-to-bucket.s3.amazonaws.com/static/css/main.9d3ee958.css" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
</body>
Но мои изображения все еще не загружались, поэтому я решил создать функцию, которая проверяла, запущено ли приложение в разработке или в производстве, и создавала URL для каждой ситуации:
export const dynamicUrl = (name) => {
let url
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
url = `../static/images/${name}`
} else {
url = `https://path-to-bucket.s3.amazonaws.com/static/images/${name}`
}
return url
};
NODE_ENV - это переменная, поставляемая с procces.env, которая проверяет окружение.
Надеюсь, это поможет кому-то, у кого та же проблема, что и у меня.