#django #vue.js #nginx
#django #vue.js #nginx
Вопрос:
У меня есть производственная настройка следующим образом;
- Серверная часть Django REST Framework
- Интерфейс Vuejs
- Контейнер Docker, который создает Vuejs для производства и копирует в папки Django Docker container / static / и / template /
- обратный прокси-сервер nginx для обработки входящих запросов
Все работает нормально, когда я перехожу на домашнюю страницу (на домашней странице нет вызовов серверного API), а затем перемещаюсь по SPA с помощью панели навигации.
У меня начинаются проблемы, когда я пытаюсь перейти непосредственно на страницу в SPA. Серверные запросы, которые должны запускаться при «создании» в Vuejs, не запускаются.
Я видел, как некоторые люди предполагают, что это связано с тем, что маршрутизатор Vue находится в режиме истории, который я хотел бы сохранить.
Основное предлагаемое решение — добавить try_files $uri $uri/ /index.html;
в конфигурацию nginx в качестве универсального. Однако, поскольку я просто проксирую все запросы в Django для обработки начальной стадии маршрутизации, и у меня уже есть все уловки в моем urls.py file ( re_path(r"^.*/$", TemplateView.as_view(template_name="index.html"), name="frontend")
) тогда, я думаю, я с этим справился.
Почему запросы API (которые запускаются при создании страницы Vuejs) будут работать при навигации с помощью маршрутизатора, но не при переходе непосредственно на страницу по URL?
конфигурация nginx
server {
listen 80;
location / {
proxy_pass http://csgs:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# try_files $uri $uri/ /index.html;
}
location /static/ {
alias /home/app/web/static/;
}
}
django urls.py узоры
urlpatterns = [
path("admin/", admin.site.urls),
path("api-auth/", include("rest_framework.urls")),
path("api/", include("api.urls")),
path("", TemplateView.as_view(template_name="index.html"), name="home"),
re_path(
r"^.*/$", TemplateView.as_view(template_name="index.html"), name="frontend"
),
]
Тело страницы SPA index:
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
Обновить:
Я ввел инструкции отладки и теперь могу сравнивать отправленный запрос и полученный ответ в каждом сценарии.
Навигация по маршрутизатору Vue:
url = api/booking/ground-stations/ user.service.js:11:16
request = {
"params": null,
"headers": {
"Authorization": "Token 164d5d7bc0fc0be90168739958c0c8640ed52f60"
}
}
response = {
"data": [
{}
],
"status": 200,
"statusText": "OK",
"headers": {
"allow": "GET, HEAD, OPTIONS",
"connection": "keep-alive",
"content-length": "82",
"content-type": "application/json",
"date": "Sat, 10 Oct 2020 21:49:58 GMT",
"referrer-policy": "same-origin",
"server": "nginx/1.17.10",
"vary": "Accept",
"x-content-type-options": "nosniff",
"x-frame-options": "DENY"
},
"config": {
"url": "api/booking/ground-stations/",
"method": "get",
"headers": {
"Accept": "application/json, text/plain, */*",
"Authorization": "Token 164d5d7bc0fc0be90168739958c0c8640ed52f60"
},
"params": null,
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
},
"request": {}
}
Обновление браузера:
url = api/booking/ground-stations/ user.service.js:11:16
request = {
"params": null,
"headers": {
"Authorization": "Token 164d5d7bc0fc0be90168739958c0c8640ed52f60"
}
}
response = {
"data": "<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/static/favicon.ico><title>CS: GS</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css><link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;800amp;display=swap" rel=stylesheet><link href=/static/css/app.d8cde755.css rel=preload as=style><link href=/static/css/chunk-vendors.93ac251e.css rel=preload as=style><link href=/static/js/app.7e344e2e.js rel=preload as=script><link href=/static/js/chunk-vendors.945fac67.js rel=preload as=script><link href=/static/css/chunk-vendors.93ac251e.css rel=stylesheet><link href=/static/css/app.d8cde755.css rel=stylesheet></head><body><noscript><strong>We're sorry but frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.945fac67.js></script><script src=/static/js/app.7e344e2e.js></script></body></html>",
"status": 200,
"statusText": "OK",
"headers": {
"connection": "keep-alive",
"content-length": "1315",
"content-type": "text/html; charset=utf-8",
"date": "Sat, 10 Oct 2020 21:51:25 GMT",
"referrer-policy": "same-origin",
"server": "nginx/1.17.10",
"x-content-type-options": "nosniff",
"x-frame-options": "DENY"
},
"config": {
"url": "api/booking/ground-stations/",
"method": "get",
"headers": {
"Accept": "application/json, text/plain, */*",
"Authorization": "Token 164d5d7bc0fc0be90168739958c0c8640ed52f60"
},
"params": null,
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
},
"request": {...}
}
Итак, теперь у меня возникает вопрос: почему обновление страницы приводит к ответу html на мои запросы внутреннего API, когда URL-адрес и запрос остаются неизменными как для маршрутизатора vuejs, так и для навигации в браузере firefox?
ОБНОВЛЕНИЕ 2: итак, мы провели еще несколько исследований. Сначала я удалил оператор catch all в URL-адресах Django и заменил его явными инструкциями;
path("", TemplateView.as_view(template_name="index.html"), name="home"),
path("ground-stations/", TemplateView.as_view(template_name="index.html"), name="gs"),
Теперь я должен видеть, когда происходят какие-либо 404s.
Теперь, когда я отправляю запрос, я вижу, что при навигации с использованием Vuejs API axios добавляет конечные точки к базовому URL «http://127.0.0.1:7100 /», что является правильным. Однако, если я обновлю страницу, базовый URL-адрес теперь станет «http://127.0.0.1/ground-stations /», и к этому добавляются конечные точки, что неверно.
Почему это происходит и как мне это исправить?
Ответ №1:
Похоже, попытка обрабатывать внешние и внутренние маршруты через django — не лучший способ сделать это.
Я нашел эту ссылку полезной: https://medium.com/@zhzhang.4390/vue-in-production-how-to-call-or-proxy-apis-f8045b5a7d16
В принципе, я мог бы использовать некоторые функции в Vue.js для прокси-вызовов API к серверной части, что я и делаю для развертывания разработки Docker. Однако мне это показалось не очень элегантным.
Вместо этого я выбрал его второй вариант разделения серверной части и интерфейса на разные контейнеры и позволил nginx выполнять маршрутизацию.
Это дало дополнительное преимущество, полностью отделив мою серверную часть Django от Vue.js передний конец. Теперь Django не нужно знать о каких-либо интерфейсных маршрутах или URL-адресах и не нужно собирать статические файлы из внешнего каталога dist.
urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("api-auth/", include("rest_framework.urls")),
path("api/", include("api.urls")),
]
интерфейс Dockerfile
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY frontend/package*.json ./
RUN npm install
COPY frontend/ .
RUN npm run build
FROM nginx:stable-alpine
COPY --from=build-stage /app/dist /home/frontend
RUN rm /etc/nginx/conf.d/default.conf
COPY deploy/prod/nginx.conf /etc/nginx/conf.d
nginx.conf
server {
listen 80;
location / {
root /home/frontend;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://csgs:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /admin {
proxy_pass http://csgs:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /static/ {
root /home/frontend;
try_files $uri $uri/ @csgs_static;
}
location @csmoc_static {
root /home/csgs;
}
}
И я использовал файл Docker Compose для монтирования статической папки из моего внутреннего контейнера Docker в /home/csgs.
Это гораздо больше похоже на настройку, подходящую для производства, чем у меня было раньше, и теперь я могу обновить страницу, и все работает так, как ожидалось.