Django Allauth / Ninja 后端 搭配 Tanstack 前端的避坑 Tips
在这篇文章中,我想与大家分享我使用 Django Allauth
无头(Headless
)后端认证,与 TanStack Start
前端结合使用的经验和心得。我今年才刚刚学习 Web 编程,定有不少错漏,还望读者指正。
演示项目代码库
https://github.com/sd44/django-allauth
项目背景
Life is short, you need Python
虽然目前前后端分离架构正被 TypeScript
一体化框架(如Next.js
/Tanstack Start
等)冲击,特别是在要求全栈协作、类型一致性、小团队敏捷项目上,但我仍爱 Python Django 的简洁清晰、快速开发和易于维护。本文无意也无力讨论架构的优劣,就此打住吧。
Django Allauth
与 NextAuth.js
, Better Auth
都提供多种认证方式(如手机号码、邮箱、通行密钥、数十种社交账户认证等)。但官方 Allauth Headless
+ React SPA
示例仍然是 JS,而非 TS 代码;网络上也缺乏Allauth
对接 Tanstack/Next SSR
前端的教程。本文便由此而生,但限于篇幅,只提出个别避坑指南,并不完整。
技术栈
- 后端:
Django
,Django Allauth
,Django Ninja
- 前端:
React
,TanStack Start/Query
,Orval
- 数据库:
Django
支持的多种数据库均可,它也提供了近乎完美的数据库迁移指令
步骤概述
1. 设置 Django 后端
安装后端
Django
和 Django Allauth
, Django Ninja
等的安装,参见官方文档和代码库 backend/pyproject.toml
, backend/mysite/settings.py
。
Django 提供总体管理,Ninja 提供其他自定义 API。其中 settings.py
中需要注意如下几点。
前后端、社交账户如 GitHub Google 等相关 URL 配置必须完全一致:
- 协议(
http
与https
不同) - 域名(
localhost
与127.0.0.1
不同) - 端口(
3000
、8000
不同) - 路径,包含或不包含最后的
/
不同
CORS 和 CSRF 安全设置
我前端服务器端口为 3000
1 | # DANGER: allow all origins |
配置 AllAuth 和 Ninja 提供 openapi 文件
安装 PyYAML 依赖,并配置如下,以便提供/_allauth/openapi.yaml
, /_allauth/openapi.json
and /_allauth/openapi.html
1 | HEADLESS_SERVE_SPECIFICATION = True # |
Ninja OpenApi 文件则是 /api/openapi.json
2. 创建前端项目
安装
前端安装见相关文档和我的代码库 frontend/package.json
,略。虽然我使用的是Tanstack Start
,但想必 Next.js
中也有类似功能实现。
通过 OpenApi 文件自动生成接口和类型
有很多软件包可以实现根据 OpenApi 文件生成相应类型和接口,我选用了功能强大的Orval
,可方便结合Axios
与Tanstack Query
,参考如下配置:
1 | const BASE_URL = 'http://localhost:8000'; |
运行orval
命令就会自动生成了
1 | bunx orval |
获取 CSRFToken
OAuth
除个别 GET
操作外,基本都需要同步传递 CSRFToken
,默认存储在 cookie 中。因此我们需要获取它,并将其放入默认的 Http 客户端实例中。
Tanstack start
提供有 getCookie
函数,我们直接使用。
1 | import { createServerFn } from '@tanstack/react-start'; |
Http Verb 携带 CSRFToken
1 | const csrftoken = await getCSRFTokenByCookie(); |
社交账户 Auth Redirect
由于社交账户认证重定向会导致用户面临重定向(302),因此此调用仅在浏览器中可用,并且必须以同步(非 XHR)方式调用, enctype
为 application/x-www-form-urlencoded
.这也就意味着Orval
生成的axios/fetch
等异步 JS 调用方式无法使用,需要我们自己实现,如下代码源于 AllAuth React-SPA
官方示例:
1 | export function postForm(action: string, data: Record<string, string>) { |
路由守卫
传统的路由守卫过于笨重,注册、登录、注销、用户认证状态等集于一身,现在已经是 5202 年了,我们使用更现代更低耦合的方法吧。幸好 Tanstack Start
提供了强大的路由功能可以方便完成以上功能。。
在根路由上下文中存储当前 session
,以获取用户状态:
1 | createTanStackRouter({ |
1 | export const Route = createRootRouteWithContext<{ |
如此,我们就可以在每个路由获取上下文中的当前 session 数据,已确定用户是否登陆。如
1 | export const Route = createFileRoute('/')({ |
除获取用户信息外 index.tsx
文件中,还有其他相关内容,这里尤其注意 登录、注销、注册等操作完成后使 queryClient
之前获取的相应数据失效,必要时也要使路由失效(例如注销操作)。可详细看下 index.tsx
, 在此不表。
总结
祝您开发过程更加顺畅。