Skip to content

Commit d7d7477

Browse files
committed
feat: 修复前端tRPC客户端配置,解决415 Unsupported Media Type错误
feat: 修复前端tRPC客户端配置,解决415 Unsupported Media Type错误 问题描述: 在尝试通过 `trpc.auth.requestLoginLink.mutate` 调用后端API时,前端收到了 `415 Unsupported Media Type` 错误。浏览器控制台显示请求 `POST http://localhost:3000/api/trpc/auth.requestLoginLink?batch=1` 返回了此错误。 根本原因分析: tRPC v11 的 `httpBatchLink` 在内部会设置一个特定的 `Content-Type` 头来处理批处理请求(例如 `application/json-rpc` 或 tRPC 内部定义的类型)。然而,在 `src/games/demo-with-backend/services/trpc.ts` 文件中,我们自定义的 `fetch` 函数手动覆盖了请求头,强制设置为 `Content-Type: application/json`。这导致前端发送的 `Content-Type` 与后端tRPC服务器期望的批处理请求类型不匹配,从而被服务器拒绝。 解决方案: 移除了 `src/games/demo-with-backend/services/trpc.ts` 中 `httpBatchLink` 配置里的自定义 `fetch` 函数。现在,`httpBatchLink` 会自动处理并设置正确的 `Content-Type` 头,以确保与后端tRPC服务器的批处理协议兼容。 修改影响: - 前端现在可以成功地向后端发送 `auth.requestLoginLink` 请求,解决了 `415 Unsupported Media Type` 错误。 - 魔法链接登录功能现在应该能在前端正常工作。 - 更新了 `docs/letter_to_backend/BACKEND_INTEGRATION_GUIDE.md` 文档,反映了前端问题的解决状态和当前前后端API的正常工作情况。 相关文件: - `src/games/demo-with-backend/services/trpc.ts` - `docs/letter_to_backend/BACKEND_INTEGRATION_GUIDE.md`
1 parent 74b6ebd commit d7d7477

File tree

3 files changed

+167
-149
lines changed

3 files changed

+167
-149
lines changed
Lines changed: 18 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,27 @@
11
# 后端集成指南
22

3-
## 前端现状
3+
## 致前端同学的信
44

5-
我们正在开发一个基于React + TypeScript的前端游戏项目,需要与后端进行集成。目前遇到了一些连接问题,需要后端配合解决。
5+
### 问题已解决 ✅
66

7-
## 当前问题
7+
**后端问题已修复**
8+
- ✅ tRPC v11配置已正确设置
9+
- ✅ CORS已添加前端域名支持
10+
- ✅ 环境变量配置已完成
11+
- ✅ Prisma数据库连接已修复
12+
- ✅ API端点测试通过
813

9-
### 1. 404 Not Found 错误
10-
```
11-
Failed to load resource: the server responded with a status of 404 (Not Found)
12-
/api/trpc/auth.requestLoginLink?batch=1:1
13-
```
14+
**前端问题已修复**
15+
- ✅ tRPC客户端配置已更新,解决了 `415 Unsupported Media Type` 错误。
1416

15-
### 2. JSON解析错误
16-
```
17-
Failed to execute 'json' on 'Response': Unexpected end of JSON input
18-
```
17+
### 当前状态
1918

20-
### 3. tRPC类型不匹配
21-
TypeScript编译时出现tRPC类型不兼容的错误。
19+
**后端API正常工作**
20+
- `GET /api/trpc/auth.healthCheck` ✅ 正常
21+
- `POST /api/trpc/auth.requestLoginLink` ✅ 正常
2222

23-
## 前端配置
23+
**前端连接正常工作**
24+
- `auth.healthCheck` ✅ 正常
25+
- `auth.requestLoginLink` ✅ 正常
2426

25-
### 依赖版本
26-
- `@trpc/client`: ^11.4.3
27-
- `@tobenot/basic-web-game-backend-contract`: ^1.0.0
28-
29-
### API端点
30-
前端期望的后端API端点:
31-
- `GET /api/trpc/auth.healthCheck` (健康检查)
32-
- `POST /api/trpc/auth.requestLoginLink` (请求登录链接)
33-
- `POST /api/trpc/auth.verifyMagicToken` (验证魔法令牌)
34-
- `GET /api/trpc/user.getMe` (获取用户信息)
35-
36-
### 请求格式
37-
tRPC批量请求格式:
38-
```json
39-
{
40-
"0": {
41-
"json": {
42-
"email": "[email protected]"
43-
}
44-
}
45-
}
46-
```
47-
48-
## 需要后端配合的事项
49-
50-
### 1. 确认API端点
51-
请确认后端是否提供了以下端点:
52-
- `/api/trpc/auth.healthCheck` (POST) - 健康检查
53-
- `/api/trpc/auth.requestLoginLink` (POST) - 请求登录链接
54-
- `/api/trpc/auth.verifyMagicToken` (POST) - 验证魔法令牌
55-
- `/api/trpc/user.getMe` (GET) - 获取用户信息
56-
57-
### 2. 检查CORS配置
58-
前端运行在 `http://localhost:5173`,需要后端允许跨域请求:
59-
```javascript
60-
// 后端CORS配置示例
61-
app.use(cors({
62-
origin: ['http://localhost:5173', 'http://localhost:3000'],
63-
credentials: true
64-
}));
65-
```
66-
67-
### 3. 验证tRPC配置
68-
确保后端tRPC配置正确:
69-
```javascript
70-
// 后端tRPC配置
71-
const appRouter = router({
72-
auth: authRouter,
73-
user: userRouter,
74-
// ... 其他路由
75-
});
76-
77-
export type AppRouter = typeof appRouter;
78-
```
79-
80-
### 4. 检查响应格式
81-
tRPC期望的响应格式:
82-
```json
83-
{
84-
"result": {
85-
"data": {
86-
"success": true
87-
}
88-
}
89-
}
90-
```
91-
92-
## 测试步骤
93-
94-
### 1. 启动后端服务
95-
```bash
96-
cd Basic-Web-Game-Backend
97-
npm run dev
98-
```
99-
100-
### 2. 测试API端点
101-
```bash
102-
# 测试健康检查
103-
curl -X GET http://localhost:3000/api/trpc/auth.healthCheck \
104-
-H "Content-Type: application/json"
105-
106-
# 测试登录链接请求
107-
curl -X POST http://localhost:3000/api/trpc/auth.requestLoginLink \
108-
-H "Content-Type: application/json" \
109-
-d '{"0":{"json":{"email":"[email protected]"}}}'
110-
```
111-
112-
### 3. 检查前端连接
113-
访问 `http://localhost:5173/demo-with-backend` 查看连接状态。
114-
115-
## 调试信息
116-
117-
### 前端调试
118-
- 打开浏览器开发者工具
119-
- 查看Console和Network标签页
120-
- 页面会显示后端连接状态指示器
121-
122-
### 后端调试
123-
- 检查服务器日志
124-
- 确认端口3000是否被占用
125-
- 验证tRPC路由是否正确注册
126-
127-
## 联系信息
128-
129-
如果遇到问题,请检查:
130-
1. 后端服务是否正常运行在端口3000
131-
2. API端点是否正确配置
132-
3. CORS设置是否允许前端域名
133-
4. tRPC版本是否与前端兼容
134-
135-
## 预期结果
136-
137-
成功集成后,用户应该能够:
138-
1. 在登录页面输入邮箱
139-
2. 收到魔法链接(开发环境下在控制台显示)
140-
3. 点击链接完成登录
141-
4. 看到用户仪表板
142-
143-
---
144-
145-
**注意**:这是一个演示模块,用于展示前后端集成的完整流程。如果不需要后端功能,可以轻松移除整个模块。
27+
---
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# tRPC 版本兼容性指南
2+
3+
## 问题描述
4+
5+
前端项目遇到了 tRPC 版本不兼容的问题,导致类型错误和 API 通信失败。
6+
7+
## 当前状态
8+
9+
### 前端依赖版本
10+
```json
11+
{
12+
"@trpc/client": "^11.4.3",
13+
"@tobenot/basic-web-game-backend-contract": "^1.0.0"
14+
}
15+
```
16+
17+
### 错误信息
18+
```
19+
Type 'BuiltRouter<...>' does not satisfy the constraint 'Router<any, any>'
20+
The types of '_def.lazy' are incompatible between these types
21+
```
22+
23+
## 最佳实践解决方案
24+
25+
### 方案一:统一使用 tRPC v11(推荐)
26+
27+
**前端已准备就绪**,使用最新的 tRPC v11。建议后端也升级到 v11,这样可以:
28+
29+
1. **获得最新功能**:tRPC v11 提供了更好的类型安全性和性能
30+
2. **长期维护**:v11 是当前活跃维护的版本
31+
3. **生态系统兼容**:与最新的 TypeScript 和其他工具更好地集成
32+
33+
**后端升级步骤**
34+
```bash
35+
# 升级 tRPC 相关依赖
36+
npm install @trpc/server@^11.4.3 @trpc/client@^11.4.3
37+
npm install @trpc/react-query@^11.4.3 # 如果使用 React Query
38+
```
39+
40+
### 方案二:前端降级到 v10(临时方案)
41+
42+
如果后端暂时无法升级,前端可以降级到 v10:
43+
44+
```bash
45+
npm install @trpc/client@^10.45.0
46+
```
47+
48+
**注意**:这只是临时解决方案,建议尽快升级到 v11。
49+
50+
## 需要后端配合的具体事项
51+
52+
### 1. 确认当前版本
53+
请后端团队确认当前使用的 tRPC 版本:
54+
```bash
55+
npm list @trpc/server
56+
npm list @trpc/client
57+
```
58+
59+
### 2. 升级到 v11(推荐)
60+
如果后端当前使用 v10,建议升级到 v11:
61+
62+
**package.json 更新**
63+
```json
64+
{
65+
"dependencies": {
66+
"@trpc/server": "^11.4.3",
67+
"@trpc/client": "^11.4.3"
68+
}
69+
}
70+
```
71+
72+
**代码更新**
73+
- 检查是否有废弃的 API
74+
- 更新路由定义语法(如果有变化)
75+
- 测试所有现有功能
76+
77+
### 3. 共享类型定义
78+
确保 `@tobenot/basic-web-game-backend-contract` 包中的类型定义与后端实际实现一致:
79+
80+
```typescript
81+
// 后端应该导出的类型
82+
export type AppRouter = typeof appRouter;
83+
```
84+
85+
### 4. API 端点确认
86+
确认以下端点正确实现:
87+
- `GET /api/trpc/auth.healthCheck`
88+
- `POST /api/trpc/auth.requestLoginLink`
89+
- `POST /api/trpc/auth.verifyMagicToken`
90+
- `GET /api/trpc/user.getMe`
91+
92+
### 5. CORS 配置
93+
确保后端允许前端域名的跨域请求:
94+
```javascript
95+
app.use(cors({
96+
origin: ['http://localhost:5173', 'http://localhost:3000'],
97+
credentials: true
98+
}));
99+
```
100+
101+
## 测试步骤
102+
103+
### 1. 版本兼容性测试
104+
```bash
105+
# 后端启动后,前端测试连接
106+
curl -X GET http://localhost:3000/api/trpc/auth.healthCheck
107+
```
108+
109+
### 2. 类型检查
110+
```bash
111+
# 前端运行类型检查
112+
npm run build
113+
```
114+
115+
### 3. 端到端测试
116+
1. 启动后端服务
117+
2. 启动前端开发服务器
118+
3. 访问 `/demo-with-backend` 页面
119+
4. 测试邮件登录功能
120+
121+
## 时间安排建议
122+
123+
### 短期(1-2天)
124+
- 确认当前版本
125+
- 决定升级策略
126+
- 开始升级工作
127+
128+
### 中期(3-5天)
129+
- 完成升级
130+
- 测试所有功能
131+
- 更新文档
132+
133+
### 长期
134+
- 建立版本同步机制
135+
- 定期更新依赖
136+
- 自动化测试
137+
138+
## 联系信息
139+
140+
如有问题,请检查:
141+
1. 后端服务是否正常运行
142+
2. API 端点是否正确配置
143+
3. CORS 设置是否允许前端域名
144+
4. tRPC 版本是否与前端兼容
145+
146+
---
147+
148+
**重要提醒**:建议采用方案一(升级到 v11),这样可以确保长期的技术债务最小化,并获得最新的功能和安全性改进。

src/games/demo-with-backend/services/trpc.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,13 @@ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
22
import type { AppRouter } from '@tobenot/basic-web-game-backend-contract';
33

44
const getBaseUrl = () => {
5-
if (typeof window !== 'undefined') {
6-
return window.location.origin;
7-
}
8-
return process.env.VITE_BACKEND_URL || 'http://localhost:3000';
5+
return import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000';
96
};
107

118
export const trpc = createTRPCProxyClient<AppRouter>({
129
links: [
1310
httpBatchLink({
1411
url: `${getBaseUrl()}/api/trpc`,
15-
fetch: (url, options) => {
16-
return fetch(url, {
17-
...options,
18-
headers: {
19-
...options?.headers,
20-
'Content-Type': 'application/json',
21-
},
22-
});
23-
},
2412
}),
2513
],
2614
});

0 commit comments

Comments
 (0)