本文主要是关于数据如何在 Django + Nuxt 应用中流动的讨论,项目源码地址: https://gitlab.com/briancaffey/django-nuxt-starter

说明

这里我们使用一个简单的 CRUD 应用程序来进行说明,该应用具有两个模型:Users 和 Posts,用户可以使用电子邮件和密码进行登录,以及创建、阅读、更新和删除博客文章(CRUD)。目前只做了 CRUD 的R(读取):列出和查看博客文章,创建、更新和删除将在以后添加,现在用户必须先登录才能查看帖子。

在 Django 中使用 Vue 的不同方法

就 Django 而言,以下是一些可以使用 Vue 的方法:

  • Vue 作为 jQuery 的替代品,用于在 Django 模板提供的视图中进行交互
  • 构建一个静态 Vue 应用程序,并将其作为 Django 项目中的一组静态资源,与其他普通 Django 模板视图提供的路由一起使用
  • 构建一个使用 Django API(通常使用 Django REST Framework)的 Vue SPA,并通过内容分发网络(CDN)提供服务
  • 使用 Vue 构建使用 Django 作为 API 的 Electron 桌面应用

在这些情况下,Vue 可以用作静态资源(例如在 CDN 上提供SPA的情况下),也可以将 Vue 代码包含在服务器的 HTML 响应中,类似于 jQuery 的用法。

使用 Nuxt 的方法

Nuxt(带有服务器端渲染或 SSR)是一个可以以几种不同方式使用的框架,我将简要讨论使用 Nuxt 的三种方式。

  1. 静态模式:此模式允许你编写内置在静态 HTML 中的 Vue 代码,然后将该 HTML 部署到 CDN 或 NGINX 之类的 Web 服务器。开发人员(或 CI/CD流程)运行命令为应用程序中的每个页面生成 HTML 文件,并且这些页面在用户访问时按原样提供。
  2. SPA 模式:类似于使用 Vue CLI 启动 Vue 项目时使用的模式。还可以像在静态模式下一样生成项目,但是生成的主要是在浏览器上执行的 Javascript 代码。
  3. SSR 模式:服务器端渲染是我将在这里重点介绍的模式。与其他使用 Vue 的方式不同,此模式涉及 Node.js 服务器,它将处理我们的请求。例如,一个 Web 请求 /posts 被发送到我们的 Nuxt 服务器(一个 Node.js 服务器进程),Node.js 负责返回包含我们要显示的所有博客帖子(或所有博客的分页选择)的 HTML。因此,Nuxt 应用必须在返回完整呈现的 HTML 页面之前向我们的 Django API 服务器发出请求 /posts 页,然后用户从 Nuxt 获取页面,读取所有博客帖子,当用户点击第2页时,我们直接从 Django API(而不是 Nuxt)请求第二页数据。然后,用户会看到一个简短的加载动画,然后是使用 AJAX(通常使用 fetch 或 axios)加载的博客文章的第二页。

Nuxt 的优缺点

将 Nuxt 用于 SSR 会在应用程序部署和我们的 Vue 代码中引入很多复杂性,但是从静态 Vue SPA 迁移到 Nuxt 时,后端 API 根本不需要更改。

仅 Django 就能为每个请求返回完全生成的,SEO 优化的 HTML,但是随着项目的不断发展,使用 Vue 和 Django 模板构建的应用程序可能会难以使用,Django/DRF + Nuxt 方法可能更适合于具有专门的后端和前端团队的项目。另一个潜在的缺点是由于“双重请求”而增加了等待时间,如果 Nuxt 服务和 Django 服务位于同一节点上,则此延迟可能问题不大。

工作流程

Nuxt Django身份验证

上图主要针对基于 session 的身份验证进行说明,重点是浏览器,Nuxt 服务器和 Django 服务器:用户使用新的浏览器访问该站点,导航到“登录”页面,使用凭据登录,然后访问一个受保护的页面: /posts ;然后用户关闭浏览器,然后直接返回 /posts 页面。

这两个操作听起来很简单,但它们涉及 Nuxt 的许多功能,这些包括:

  • asyncData
  • nuxtServerInit
  • 客户端和服务器上的 Vuex
  • 自定义插件的 axios

该图尚未涉及的 Nuxt 的一些重要部分包括:

  • Nuxt auth 模块
  • Nuxt 中间件等

用户场景一:用户尝试打开 /posts ,重定向到 /login ,登录,然后导航到 /posts 并查看博客文章

  1. 用户导航到 http://domain.com/ ,该请求由 Nuxt 服务器处理。
  2. 该动作称为 nuxtServerInit ,这是一种特殊的 Vuex 动作,如果在项目中定义 store/index.js ,则每次对Nuxt 服务器的请求都会调用一次(在浏览器中最初访问页面或刷新页面时)。
  3. nuxtServerInit 在名为 user 的模块中调用 Vuex 操作 fetchData ,此操作 /api/account/ 向 Django 应用中发出 GET 请求。
  4. /api/account/ 直接从 Docker 的 Nuxt 容器对 Django 后端进行 API 调用( backend:8000
  5. 如果请求是由匿名用户发出的(没有用户登录),则将403响应返回到 Nuxt 服务器,并且未在 Vuex 存储中设置任何帐户数据。
  6. 由于用户当前未登录,因此该请求返回403响应。
  7. authMiddleware (在 Nuxt 服务器上) /login 根据 Vuex 存储中的 authenticated 值将用户重定向到原始的请求 /posts 返回一个完全渲染的 /login/ 页面。
  8. 用户现在位于登录页面上。
  9. created 登录页面的 hook 向 /api/login-set-cookie/ 发出 GET 请求。

10、11.此端点调用视图的注解 @ensure_csrf_token

  1. 当响应返回到浏览器时, csrftoken 将在浏览器中进行设置。

  2. $apiCall 函数定义在 plugins/axios.js 中,并将 csrftoken cookie 添加到 API 请求的 X-CSRF-Token 的 Header 中,这对于需要 CSRF Token 的 POST 请求非常重要,当用户在登录表单中填写其 email 和密码时,将使用 /api/login/`` 和 email/密码作为凭据 $apiCall` 函数。

  3. email 和密码作为 POST 请求中的数据发送到 /api/login/

15、16. /api/login/ URL 调用 login_view ,它使用 django.contrib.auth 中两个函数: authenticate login authenticate 从提供的email/密码中获取用户,然后该 login 函数 sessionid 在响应上设置一个HttpOnly 会话 cookie。

  1. sessionid /api/login/`请求成功返回时,将在浏览器上自动设置HttpOnly cookie 。

  2. 当此 /api/login/ 请求成功返回时,将 auth 设置 Vuex 模块中的值以跟踪当前用户的登录状态。

  3. 接下来,向 /api/account/ 发出 GET 请求。

20、21. 由于 sessionid cookie 会自动设置并随请求一起发送,因此该请求将成功。

  1. /api/account/ 请求返回时,用户的账户信息被保存到 user 这个 Vuex 模块。此时,客户端可以自动重定向到主页,用户帐户页面,仪表板等。

  2. 现在登录,用户导航(再次通过 Vue 路由器)到 /posts ,该页面显示所有博客文章的分页视图。

  3. 此页面具有 asyncData 创建页面组件并调度 Vuex 操作时调用的方法 posts/fetchData

  4. 此 Vuex 操作向发出 GET 请求 /api/posts/

26,27. /api/posts/ 调用 ModelViewSet 并返回博客文章的分页列表。

  1. /api/posts/ 请求返回成功,博客文章的数据保存到 blog Vuex模块。

用户场景二:登录的用户打开新的浏览器窗口并重新访问 /posts

  1. 用户关闭其浏览器,然后打开一个新的浏览器窗口并导航到 /posts
  2. nuxtServerInit 像往常一样被调用
  3. user/fetchData 动作被调用,此操作向发出 GET 请求 /api/account/
  4. /api/account/ 请求成功返回,会话 ID cookie 从浏览器传递到从 Nuxt 服务器向后端 API(/api/account/)发出的 API 请求。然后在 Vuex 用户模块上设置用户帐户数据。
  5. 页面的 asyncData 方法 /posts 被调用。
  6. asyncData 分发 Vuex 动作 posts/fetchData`
  7. posts/fetchData /api/posts/ 发出 API 请求。
  8. /api/posts/ 请求由模型 Post 的 ModelViewSet 处理,该模型获取博客帖子,然后在请求返回响应(至 Nuxt服务器)时将其设置到 Vuex 存储(在服务器上)。
  9. 异步数据获取完成后(/posts 页面为 nuxtServerInit 和 asyncData),将使用服务器上存储的 Vuex 存储数据来呈现 HTML 页面。Vuex 数据与呈现的 HTML 一起返回。
  10. 最后,用户看到博客文章列表,该页面“立即”加载;最初加载页面后无需等待数据加载。

下一步

下一步是准备在我有时间的时候将其部署到生产环境中,现在在本地设置运行良好,并且我认为它对于简单的DigitalOcean Docker 集群部署也应该很好,就像我在其他 Django + Vue 项目中所做的那样。

我还想添加帖子的创建,更新和删除功能,通过 API 调用改善错误处理,添加表单验证,也许还可以使用 Jest 编写一些测试🗣

原文链接:https://briancaffey.github.io/2021/01/01/session-authentication-with-django-django-rest-framework-and-nuxt/


Django R Vue ES Framework 身份验证 Nuxt