问题
前端调 FastAPI 的 /document/parse-path 接口,直接甩回来这么一坨:


HTTP 422 Unprocessable Entity 。不是 500 后端炸了,是请求体根本没通过验证。
报错解释
|--------------------|------------------------------------------------|
| 字段 | 含义 |
| 422 | FastAPI/Pydantic 的请求体验证失败(请求格式问题) |
| json_invalid | JSON 本身解析就挂了,还没到 Pydantic 那一步 |
| Invalid \\escape | JSON 里反斜杠 \ 只能当转义前缀(\n \t \\ \uXXXX),不能瞎用。 |
情况是这样的。 前端传了一个 Windows 绝对路径:
{ "file_path": "D:\data\uploads\report.pdf" }
JSON 解析器看到 \u 以为是 unicode 转义的开头(\uXXXX),实际并不是,所以直接炸。
原因
JSON 转义报错是表面问题 ,本质是之前后端逼前端传Windows绝对路径 ------而它天生带\,踩JSON转义坑是必不可免的,就算这次让前端转义成\\,下次换个前端 / 工具还会栽。
解决
加路径分流 :让前端可以根本不传带反斜杠的路径,只传裸文件名就行------连反斜杠都碰不到,转义问题直接没了,顺便还让前端不用关心后端上传目录,接口更友好。
改动
改了 3 处,全在 document.py 里:
改动 1:新增路径分流逻辑
java
input_path = body.file_path
if os.path.dirname(input_path):
# dirname 返回非空 → 传的是完整路径(带目录),直接用
target_path = input_path
else:
# dirname 返回空串 → 只有裸文件名,拼到上传目录
upload_dir = _ensure_upload_dir()
target_path = os.path.join(upload_dir, input_path)
关键函数 os.path.dirname()一行解决跨平台判断:
os.path.dirname(path) :返回路径中"最后一个斜杠之前"的部分,也就是目录部分。
|--------------------------------|---------------------|--------|
| 输入 | dirname() 返回 | 说明 |
| "D:\data\uploads\report.pdf" | "D:\data\uploads" | ✅ 完整路径 |
| "data/uploads/report.pdf" | "data/uploads" | ✅ 完整路径 |
| "report.pdf" | ""(空串) | ❌ 裸文件名 |
改动 2:新增 _ensure_upload_dir() 辅助函数
java
def _ensure_upload_dir():
"""确保上传目录存在"""
upload_dir = Path(settings.UPLOAD_DIR) # 获取上传目录路径
upload_dir.mkdir(parents=True, exist_ok=True) # 创建目录
return upload_dir
parents=True:如果data/uploads/里上级目录不存在,递归创建 (类似mkdir -p);exist_ok=True:目录已经存在也不报错
改动 3:提前校验文件是否存在
java
if not os.path.isfile(target_path):
raise HTTPException(status_code=404, detail=f"文件不存在: {target_path}")
原来直接扔给解析器 → 解析器抛异常 → 给 500。现在提前拦截,给调用方一个清晰的 404 ✨
完整调用流程
POST /document/parse-path
body: { "file_path": "report.pdf" } ← 裸文件名,根本没 \ !
│
▼
os.path.dirname("report.pdf") → "" ← falsy!走 else 分支
│
▼
_ensure_upload_dir()
Path(settings.UPLOAD_DIR) + mkdir(parents, exist_ok)
│
▼
os.path.join(upload_dir, "report.pdf")
→ "data/uploads/report.pdf" ← 跨平台安全拼接
│
▼
os.path.exists()? ──NO──▶ 404 错误(友好提示)
│
YES
│
▼
parse_file(target_path) 解析
│
▼
返回 chunks / tables / 统计信息
💡 思路总结
以后写++"接收路径参数"的接口++,按这个顺序过一遍:
- 调用方可能传什么? → 完整绝对路径?还是裸文件名?
- 怎么区分? →
os.path.dirname() - 裸文件名去哪找? → 拼到
settings里预先配置的上传目录(不要硬编码) - 目录不存在咋办? →
pathlib.Path.mkdir(parents=True, exist_ok=True)自动建,抽个辅助函数 - 文件不存在咋办? → 提前检查,返回 404 而不是让解析器炸 500
就这 5 步,本质是给调用方提供了两种输入方式。

