# 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设计应避免歧义路径

---

**状态**：✅ 已修复，可以正常使用

