本文主要是关于数据如何在 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 的三种方式。
-
静态模式:此模式允许你编写内置在静态 HTML 中的 Vue 代码,然后将该 HTML 部署到 CDN 或 NGINX 之类的 Web 服务器。开发人员(或 CI/CD流程)运行命令为应用程序中的每个页面生成 HTML 文件,并且这些页面在用户访问时按原样提供。
-
SPA 模式:类似于使用 Vue CLI 启动 Vue 项目时使用的模式。还可以像在静态模式下一样生成项目,但是生成的主要是在浏览器上执行的 Javascript 代码。
-
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 服务位于同一节点上,则此延迟可能问题不大。
工作流程
上图主要针对基于 session 的身份验证进行说明,重点是浏览器,Nuxt 服务器和 Django 服务器:用户使用新的浏览器访问该站点,导航到“登录”页面,使用凭据登录,然后访问一个受保护的页面:
/posts
;然后用户关闭浏览器,然后直接返回
/posts
页面。
这两个操作听起来很简单,但它们涉及 Nuxt 的许多功能,这些包括:
该图尚未涉及的 Nuxt 的一些重要部分包括:
用户场景一:用户尝试打开
/posts
,重定向到
/login
,登录,然后导航到
/posts
并查看博客文章
-
用户导航到
http://domain.com/
,该请求由 Nuxt 服务器处理。
-
该动作称为
nuxtServerInit
,这是一种特殊的 Vuex 动作,如果在项目中定义
store/index.js
,则每次对Nuxt 服务器的请求都会调用一次(在浏览器中最初访问页面或刷新页面时)。
-
nuxtServerInit
在名为
user
的模块中调用 Vuex 操作
fetchData
,此操作
/api/account/
向 Django 应用中发出 GET 请求。
-
/api/account/
直接从 Docker 的 Nuxt 容器对 Django 后端进行 API 调用(
backend:8000
)
-
如果请求是由匿名用户发出的(没有用户登录),则将403响应返回到 Nuxt 服务器,并且未在 Vuex 存储中设置任何帐户数据。
-
-
authMiddleware
(在 Nuxt 服务器上)
/login
根据 Vuex 存储中的
authenticated
值将用户重定向到原始的请求
/posts
返回一个完全渲染的
/login/
页面。
-
-
created
登录页面的 hook 向
/api/login-set-cookie/
发出 GET 请求。
10、11.此端点调用视图的注解
@ensure_csrf_token
。
-
当响应返回到浏览器时,
csrftoken
将在浏览器中进行设置。
-
$apiCall
函数定义在
plugins/axios.js
中,并将 csrftoken cookie 添加到 API 请求的 X-CSRF-Token 的 Header 中,这对于需要 CSRF Token 的 POST 请求非常重要,当用户在登录表单中填写其 email 和密码时,将使用
/api/login/`` 和 email/密码作为凭据
$apiCall` 函数。
-
email 和密码作为 POST 请求中的数据发送到
/api/login/
。
15、16.
/api/login/
URL 调用
login_view
,它使用 django.contrib.auth 中两个函数:
authenticate
和
login
。
authenticate
从提供的email/密码中获取用户,然后该
login
函数
sessionid
在响应上设置一个HttpOnly 会话 cookie。
-
sessionid
当
/api/login/`请求成功返回时,将在浏览器上自动设置HttpOnly cookie 。
-
当此
/api/login/
请求成功返回时,将
auth
设置 Vuex 模块中的值以跟踪当前用户的登录状态。
-
接下来,向
/api/account/
发出 GET 请求。
20、21. 由于
sessionid
cookie 会自动设置并随请求一起发送,因此该请求将成功。
-
当
/api/account/
请求返回时,用户的账户信息被保存到
user
这个 Vuex 模块。此时,客户端可以自动重定向到主页,用户帐户页面,仪表板等。
-
现在登录,用户导航(再次通过 Vue 路由器)到
/posts
,该页面显示所有博客文章的分页视图。
-
此页面具有
asyncData
创建页面组件并调度 Vuex 操作时调用的方法
posts/fetchData
。
-
此 Vuex 操作向发出 GET 请求
/api/posts/
。
26,27.
/api/posts/
调用
ModelViewSet
并返回博客文章的分页列表。
-
当
/api/posts/
请求返回成功,博客文章的数据保存到
blog
Vuex模块。
用户场景二:登录的用户打开新的浏览器窗口并重新访问
/posts
-
用户关闭其浏览器,然后打开一个新的浏览器窗口并导航到
/posts
。
-
-
该
user/fetchData
动作被调用,此操作向发出 GET 请求
/api/account/
。
-
/api/account/
请求成功返回,会话 ID cookie 从浏览器传递到从 Nuxt 服务器向后端 API(/api/account/)发出的 API 请求。然后在 Vuex 用户模块上设置用户帐户数据。
-
页面的
asyncData
方法
/posts
被调用。
-
asyncData
分发 Vuex 动作
posts/fetchData`
-
posts/fetchData
向
/api/posts/
发出 API 请求。
-
/api/posts/ 请求由模型 Post 的 ModelViewSet 处理,该模型获取博客帖子,然后在请求返回响应(至 Nuxt服务器)时将其设置到 Vuex 存储(在服务器上)。
-
异步数据获取完成后(/posts 页面为 nuxtServerInit 和 asyncData),将使用服务器上存储的 Vuex 存储数据来呈现 HTML 页面。Vuex 数据与呈现的 HTML 一起返回。
-
最后,用户看到博客文章列表,该页面“立即”加载;最初加载页面后无需等待数据加载。
下一步
下一步是准备在我有时间的时候将其部署到生产环境中,现在在本地设置运行良好,并且我认为它对于简单的DigitalOcean Docker 集群部署也应该很好,就像我在其他 Django + Vue 项目中所做的那样。
我还想添加帖子的创建,更新和删除功能,通过 API 调用改善错误处理,添加表单验证,也许还可以使用 Jest 编写一些测试🗣
原文链接:https://briancaffey.github.io/2021/01/01/session-authentication-with-django-django-rest-framework-and-nuxt/