Appearance
前后端联调
前端页面和后端接口各自跑通了,合到一起时大概率遇到各种问题:跨域报错、登录后 Token 带不上、字段名对不上、请求发了但数据不对。这些联调时常见的坑和调试方法,集中过一遍。
一、CORS——跨域请求被拦
前端 localhost:5173 调后端 localhost:8000,浏览器控制台报:
text
Access to fetch at 'http://localhost:8000/api/assets' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header原因:端口不一样算跨域,浏览器要求后端在响应头里明确说"我允许 localhost:5173 访问"。后端 CORS 中间件配了 allow_origins=["http://localhost:5173"] 才会带这个头。
后端确认配置(17 篇讲过):
python
# backend/app/main.py
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # 前端开发服务器
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)排查方法:浏览器 Network 面板,找到被拦的请求,看 Response Headers 里有没有 Access-Control-Allow-Origin。没有就是后端没配 CORS,或者 allow_origins 里没写前端地址。
二、Token 传递的完整链路
登录后前端拿到 Token,后续请求要带上。整个链路:
每个环节出问题的表现:
| 环节 | 表现 | 排查 |
|---|---|---|
| 登录接口返回没 Token | 前端存不进去,后续全 401 | 后端日志看 /login 有没有报错 |
| Token 没存进 localStorage | Network 看请求头没 Authorization | 浏览器 Console 执行 localStorage.getItem("access_token") |
| Token 存了但请求没带 | Network 看请求头没 Authorization | 检查 request 函数有没有读 Token 放进 headers |
| Token 带了但后端不认 | 401 Unauthorized | Token 过期、SECRET_KEY 前后端不一致、Token 格式错 |
最有效的排查方法:浏览器 F12 → Network 面板 → 点失败的请求 → 看 Request Headers 里 Authorization 字段。有就是带了,没有就是 request 函数有问题。带了还是 401,就是 Token 本身的问题。
三、字段名不一致
后端返回 {"hostname": "web-01"},前端读 asset.host_name(下划线位置不一样),页面显示空白。
这种问题不会报错——接口正常返回 200,但页面没数据。最隐蔽的一类 bug。
排查方法:Network 面板看 Response——后端实际返回的 JSON 字段名是什么,跟前端代码里用的字段名逐个对比。
js
// 后端返回
{"id": 1, "hostname": "web-01", "ip": "192.168.1.10"}
// 前端代码
<td>{{ asset.hostname }}</td> // ✅ 对上了
<td>{{ asset.host_name }}</td> // ❌ 多了下划线,显示空白预防:后端 Schema 和前端 TypeScript 类型定义保持一致(如果用了 TypeScript)。没用 TypeScript 就靠仔细对照——或在前端 API 函数里做一层字段映射。
四、POST 请求体格式
前端发 POST 请求,后端收到的请求体是空的或格式不对:
js
// 常见错误:没设 Content-Type,或 body 没转 JSON
fetch("/api/assets", {
method: "POST",
body: { hostname: "web-01" }, // ❌ 直接传对象,fetch 不认
});
// 正确
fetch("/api/assets", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ hostname: "web-01" }), // ✅ 转成 JSON 字符串
});fetch 的 body 只接受字符串。JS 对象要 JSON.stringify 转成 JSON 字符串,同时设 Content-Type: application/json 告诉后端"我发的是 JSON"。06 篇封装的 request 函数已经处理了这两件事,用它就不会踩这个坑。
五、登录接口的特殊格式
FastAPI 的 OAuth2PasswordBearer 默认从表单格式(application/x-www-form-urlencoded)拿用户名密码,不是 JSON。前端登录请求要发表单格式:
js
// ❌ JSON 格式——FastAPI 的 OAuth2 表单不认
fetch("/api/users/login", {
body: JSON.stringify({ username: "admin", password: "123" }),
});
// ✅ 表单格式
fetch("/api/users/login", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "username=admin&password=123",
});这个不一致很容易踩——后端用了 OAuth2PasswordBearer,但前端习惯性发 JSON。要么前端改表单格式,要么后端用 Pydantic 模型接收 JSON 请求体(不用 OAuth2 标准表单)。24 篇的登录 API 封装里已经用了表单格式。
六、调试工具
| 工具 | 用途 |
|---|---|
| 浏览器 F12 Network | 看每个请求的 URL、方法、请求头、响应体、状态码 |
| 浏览器 Console | 看 JS 报错、打印变量值 |
| 后端日志 | 看请求有没有到后端、后端处理有没有报错 |
| curl | 不走前端,直接测后端接口——排除前端问题 |
| FastAPI /docs | 交互式文档,直接在页面上测每个接口 |
联调时最常用的排查路径:先看 Network 面板(请求发了没?状态码多少?响应体长啥样?)→ 再看 Console(前端 JS 有没有报错?)→ 最后看后端日志(请求到后端了没?后端有没有异常?)。沿着这条线查,绝大多数联调问题都能定位。
七、联调检查清单
前后端都跑起来后,按这个清单逐项验证:
- [ ] 前端能访问后端(Network 看到请求发出去了,没有 CORS 报错)
- [ ] 登录能拿到 Token(Network 看
/login返回里有access_token) - [ ] Token 存进了 localStorage(Console 执行
localStorage.getItem("access_token")) - [ ] 后续请求带了 Authorization 头(Network 看请求头)
- [ ] 列表页能加载到数据(Network 看
/assets返回 200,有数据) - [ ] 创建操作成功(Network 看 POST 返回 200,数据库里有新记录)
- [ ] 删除操作成功(Network 看 DELETE 返回 200,列表刷新后记录消失)
- [ ] 事件日志有记录(每次创建/删除后,事件页能看到对应记录)
全通过,前后端就联调完成了。后面几篇做任务调度、Docker 打包、部署上线。