【问题标题】:Configure Vue.js to allow browser refreshes (django backend, nginx server)配置 Vue.js 以允许浏览器刷新(django 后端、nginx 服务器)
【发布时间】:2021-01-25 13:04:58
【问题描述】:

我的生产设置如下;

  • Django REST 框架后端
  • Vuejs 前端
  • 为生产构建 Vuejs 并复制到 Django Docker 容器 /static/ 和 /template/ 文件夹的 Docker 容器
  • nginx 反向代理处理传入请求

当我导航到主页(主页上没有后端 API 调用)然后使用导航抽屉在 SPA 中导航时,一切正常。

当我尝试直接转到 SPA 中的页面时,我开始遇到问题。应该在 Vuejs 中“创建”时触发的后端请求没有触发。

我看到有人认为这与 Vue 路由器处于历史模式有关,我想保留它。

建议的主要补救措施是将try_files $uri $uri/ /index.html; 作为全部添加到 nginx 配置中。但是,由于我只是将所有请求代理到 Django 以处理路由的初始阶段,并且我的 urls.py 文件 (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 索引页面正文:

<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;800&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": {...}
}

所以我的问题现在变成了;当 vuejs 路由器和 firefox 浏览器导航的 url 和请求保持不变时,为什么刷新页面会导致对我的后端 API 查询的 html 响应?

更新 2: 所以做了一些更多的调查。首先,我删除了 Django url 中的 catch all 语句并替换为显式语句;

path("", TemplateView.as_view(template_name="index.html"), name="home"),
    path("ground-stations/", TemplateView.as_view(template_name="index.html"), name="gs"),

现在我应该看看何时发生任何 404。

现在,当我发送请求时,我可以看到在使用 Vuejs API 进行导航时,axios 会将端点附加到基本 URL“http://127.0.0.1:7100/”,这是正确的。但是,如果我刷新页面,则基本 url 现在变为“http://127.0.0.1/ground-stations/”,并且端点会附加到此页面上,这是不正确的。

为什么会发生这种情况,我该如何解决?

【问题讨论】:

    标签: django vue.js nginx


    【解决方案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。

    与以前相比,这感觉更像是适合生产的设置,现在我可以刷新页面,一切都按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-21
      • 2015-02-15
      • 1970-01-01
      • 2020-05-23
      • 1970-01-01
      • 2015-04-18
      • 1970-01-01
      • 2021-11-17
      相关资源
      最近更新 更多