- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
229 lines
5.3 KiB
Markdown
229 lines
5.3 KiB
Markdown
# CORS跨域错误修复记录
|
||
|
||
**日期**:2025-10-16
|
||
**问题**:前端访问tasks API时出现CORS跨域错误
|
||
**状态**:✅ 已修复
|
||
|
||
---
|
||
|
||
## 错误信息
|
||
|
||
```
|
||
Access to XMLHttpRequest at 'http://localhost:8000/api/v1/manager/tasks/?status=ongoing'
|
||
(redirected from 'http://localhost:8000/api/v1/manager/tasks?status=ongoing')
|
||
from origin 'http://localhost:3001' has been blocked by CORS policy:
|
||
No 'Access-Control-Allow-Origin' header is present on the requested resource.
|
||
```
|
||
|
||
## 问题分析
|
||
|
||
### 根本原因
|
||
|
||
1. **307重定向问题**:
|
||
- 前端请求:`/api/v1/manager/tasks` (无斜杠)
|
||
- FastAPI重定向到:`/api/v1/manager/tasks/` (有斜杠)
|
||
- 状态码:307 Temporary Redirect
|
||
|
||
2. **CORS预检失败**:
|
||
- 浏览器发送CORS预检请求(OPTIONS)
|
||
- 服务器返回307重定向
|
||
- 浏览器不会在预检请求后跟随重定向
|
||
- 导致CORS错误
|
||
|
||
3. **FastAPI默认行为**:
|
||
- FastAPI默认会自动在URL末尾添加斜杠
|
||
- 当路由定义为 `@router.get("/")` 且prefix="/manager/tasks"时
|
||
- 实际路径是 `/api/v1/manager/tasks/`
|
||
- 访问 `/api/v1/manager/tasks` 会触发307重定向
|
||
|
||
### 为什么CORS配置正确但仍然报错?
|
||
|
||
虽然后端的CORS配置包含了 `http://localhost:3001`:
|
||
|
||
```python
|
||
CORS_ORIGINS: list[str] = Field(
|
||
default=[
|
||
"http://localhost:3000",
|
||
"http://localhost:3001", # ✅ 已配置
|
||
"http://localhost:5173",
|
||
...
|
||
]
|
||
)
|
||
```
|
||
|
||
但是307重定向响应没有包含CORS头部,导致浏览器阻止跟随重定向。
|
||
|
||
---
|
||
|
||
## 解决方案
|
||
|
||
### 修改内容
|
||
|
||
**文件**:`kaopeilian-backend/app/api/v1/tasks.py`
|
||
|
||
#### 修改1:禁用自动重定向
|
||
|
||
```python
|
||
# 修改前:
|
||
router = APIRouter(prefix="/manager/tasks", tags=["Tasks"])
|
||
|
||
# 修改后:
|
||
router = APIRouter(prefix="/manager/tasks", tags=["Tasks"], redirect_slashes=False)
|
||
```
|
||
|
||
#### 修改2:修改路由路径
|
||
|
||
```python
|
||
# 修改前:
|
||
@router.get("/", response_model=...)
|
||
@router.post("/", response_model=...)
|
||
|
||
# 修改后:
|
||
@router.get("", response_model=...) # 空字符串而非 "/"
|
||
@router.post("", response_model=...)
|
||
```
|
||
|
||
### 修改原理
|
||
|
||
1. **redirect_slashes=False**:
|
||
- 告诉FastAPI不要自动添加/移除尾部斜杠
|
||
- 避免触发307重定向
|
||
|
||
2. **使用空字符串 ""**:
|
||
- `prefix="/manager/tasks"` + `@router.get("")`
|
||
- 最终路径:`/api/v1/manager/tasks` (精确匹配,无斜杠)
|
||
- 避免歧义和重定向
|
||
|
||
---
|
||
|
||
## 验证结果
|
||
|
||
### 测试前(有重定向)
|
||
|
||
```bash
|
||
$ curl -I http://localhost:8000/api/v1/manager/tasks
|
||
HTTP/1.1 307 Temporary Redirect
|
||
location: http://localhost:8000/api/v1/manager/tasks/
|
||
```
|
||
|
||
### 测试后(无重定向)
|
||
|
||
```bash
|
||
$ curl -I http://localhost:8000/api/v1/manager/tasks
|
||
HTTP/1.1 401 Unauthorized # 因为没有token,这是预期行为
|
||
```
|
||
|
||
✅ **没有307重定向,直接返回响应**
|
||
|
||
---
|
||
|
||
## 相关知识点
|
||
|
||
### 1. CORS预检请求(Preflight Request)
|
||
|
||
浏览器在发送跨域请求前,会先发送OPTIONS请求检查:
|
||
- 服务器是否允许该源(Origin)
|
||
- 是否允许该HTTP方法
|
||
- 是否允许该请求头
|
||
|
||
**关键**:预检请求不会跟随重定向!
|
||
|
||
### 2. FastAPI斜杠处理
|
||
|
||
FastAPI默认行为:
|
||
- `/path` 和 `/path/` 被视为不同的路径
|
||
- 如果定义了 `/path/`,访问 `/path` 会重定向
|
||
- 如果定义了 `/path`,访问 `/path/` 也会重定向
|
||
|
||
### 3. 为什么其他API没问题?
|
||
|
||
其他API可能:
|
||
- 使用了完整路径(如 `/api/v1/exams/start`)
|
||
- 前端调用时带了尾部斜杠
|
||
- 路由定义使用了具体路径而非 "/"
|
||
|
||
---
|
||
|
||
## 最佳实践
|
||
|
||
### 1. API路由定义规范
|
||
|
||
**推荐**:
|
||
```python
|
||
# 对于列表/集合类资源
|
||
router = APIRouter(prefix="/manager/tasks", redirect_slashes=False)
|
||
@router.get("") # 获取列表
|
||
@router.post("") # 创建新项
|
||
|
||
# 对于具体资源
|
||
@router.get("/{task_id}") # 获取详情
|
||
@router.put("/{task_id}") # 更新
|
||
@router.delete("/{task_id}") # 删除
|
||
```
|
||
|
||
**避免**:
|
||
```python
|
||
# 避免使用 "/" 作为路径,容易引起重定向
|
||
@router.get("/")
|
||
@router.post("/")
|
||
```
|
||
|
||
### 2. 前后端API路径约定
|
||
|
||
- 后端定义什么路径,前端就用什么路径
|
||
- 统一不使用尾部斜杠(推荐)
|
||
- 或统一使用尾部斜杠
|
||
- **避免混用**
|
||
|
||
### 3. FastAPI Router配置
|
||
|
||
```python
|
||
router = APIRouter(
|
||
prefix="/api/path",
|
||
tags=["Tag"],
|
||
redirect_slashes=False # 推荐添加,避免意外重定向
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 影响范围
|
||
|
||
**修改的文件**:
|
||
- `kaopeilian-backend/app/api/v1/tasks.py`
|
||
|
||
**影响的API**:
|
||
- GET `/api/v1/manager/tasks` - 获取任务列表
|
||
- POST `/api/v1/manager/tasks` - 创建任务
|
||
- GET `/api/v1/manager/tasks/stats` - 任务统计
|
||
- GET `/api/v1/manager/tasks/{id}` - 任务详情
|
||
- PUT `/api/v1/manager/tasks/{id}` - 更新任务
|
||
- DELETE `/api/v1/manager/tasks/{id}` - 删除任务
|
||
|
||
**测试确认**:
|
||
- ✅ 后端服务正常启动
|
||
- ✅ API不再返回307重定向
|
||
- ✅ CORS配置生效
|
||
- ✅ 前端可以正常调用
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
**问题**:FastAPI默认的斜杠重定向导致CORS预检失败
|
||
|
||
**解决**:
|
||
1. 添加 `redirect_slashes=False` 配置
|
||
2. 使用空字符串 `""` 而非 `"/"` 作为路由路径
|
||
|
||
**经验**:
|
||
- CORS问题不一定是CORS配置错误
|
||
- 307重定向会破坏CORS预检流程
|
||
- FastAPI的路由斜杠处理需要特别注意
|
||
- API设计应避免歧义路径
|
||
|
||
---
|
||
|
||
**状态**:✅ 已修复,可以正常使用
|
||
|