- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
5.3 KiB
5.3 KiB
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.
问题分析
根本原因
-
307重定向问题:
- 前端请求:
/api/v1/manager/tasks(无斜杠) - FastAPI重定向到:
/api/v1/manager/tasks/(有斜杠) - 状态码:307 Temporary Redirect
- 前端请求:
-
CORS预检失败:
- 浏览器发送CORS预检请求(OPTIONS)
- 服务器返回307重定向
- 浏览器不会在预检请求后跟随重定向
- 导致CORS错误
-
FastAPI默认行为:
- FastAPI默认会自动在URL末尾添加斜杠
- 当路由定义为
@router.get("/")且prefix="/manager/tasks"时 - 实际路径是
/api/v1/manager/tasks/ - 访问
/api/v1/manager/tasks会触发307重定向
为什么CORS配置正确但仍然报错?
虽然后端的CORS配置包含了 http://localhost:3001:
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:禁用自动重定向
# 修改前:
router = APIRouter(prefix="/manager/tasks", tags=["Tasks"])
# 修改后:
router = APIRouter(prefix="/manager/tasks", tags=["Tasks"], redirect_slashes=False)
修改2:修改路由路径
# 修改前:
@router.get("/", response_model=...)
@router.post("/", response_model=...)
# 修改后:
@router.get("", response_model=...) # 空字符串而非 "/"
@router.post("", response_model=...)
修改原理
-
redirect_slashes=False:
- 告诉FastAPI不要自动添加/移除尾部斜杠
- 避免触发307重定向
-
使用空字符串 "":
prefix="/manager/tasks"+@router.get("")- 最终路径:
/api/v1/manager/tasks(精确匹配,无斜杠) - 避免歧义和重定向
验证结果
测试前(有重定向)
$ curl -I http://localhost:8000/api/v1/manager/tasks
HTTP/1.1 307 Temporary Redirect
location: http://localhost:8000/api/v1/manager/tasks/
测试后(无重定向)
$ 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路由定义规范
推荐:
# 对于列表/集合类资源
router = APIRouter(prefix="/manager/tasks", redirect_slashes=False)
@router.get("") # 获取列表
@router.post("") # 创建新项
# 对于具体资源
@router.get("/{task_id}") # 获取详情
@router.put("/{task_id}") # 更新
@router.delete("/{task_id}") # 删除
避免:
# 避免使用 "/" 作为路径,容易引起重定向
@router.get("/")
@router.post("/")
2. 前后端API路径约定
- 后端定义什么路径,前端就用什么路径
- 统一不使用尾部斜杠(推荐)
- 或统一使用尾部斜杠
- 避免混用
3. FastAPI Router配置
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预检失败
解决:
- 添加
redirect_slashes=False配置 - 使用空字符串
""而非"/"作为路由路径
经验:
- CORS问题不一定是CORS配置错误
- 307重定向会破坏CORS预检流程
- FastAPI的路由斜杠处理需要特别注意
- API设计应避免歧义路径
状态:✅ 已修复,可以正常使用