frontend-bug: 合同方案附件上传 MultipartException 没用 FormData (@mmg)

这个提交包含在:
API Changelog Bot 2026-05-04 11:22:46 +08:00
父节点 8dd2323197
当前提交 1c69282e56

查看文件

@ -0,0 +1,97 @@
---
date: 2026-05-04
type: frontend-bug
module: contract-scheme-attachment
priority: high
notify: ["@mmg"]
status: pending
---
# 合同方案附件上传 MultipartException — 前端没用 FormData
## 现象
POST `/admin/contract/scheme/{schemeId}/attachments` 报错:
```json
{"code":500,"message":"服务器内部错误[MultipartException]: Current request is not a multipart request","data":null,"success":false}
```
## 根因
后端 round-trip 5MB / 中文名 / 30MB PDF curl 上传**全部成功**(已验证),后端 100% OK。
错误是 **`org.springframework.web.multipart.MultipartException`** — Spring 在期望 multipart 请求体时拿到的不是 multipart 格式。这是前端发请求时 **Content-Type / body 包装错了**
最常见原因(按概率):
1. **axios 默认 Content-Type=application/json,没让 browser 自动设成 multipart**
2. **传了普通 object 不是 FormData,axios JSON.stringify 了**
3. **代码里写了 `Content-Type: application/json` 强制覆盖**
## 修复(前端)
### 错误写法 ❌
```js
// 错: 直接传对象 → axios 自动 JSON.stringify
http.post(`/admin/contract/scheme/${schemeId}/attachments`, {
file: fileObject,
fileType: 'PDF',
sortOrder: 0
})
// 错: 强制 Content-Type
http.post(url, formData, {
headers: { 'Content-Type': 'application/json' }
})
```
### 正确写法 ✅
```js
const formData = new FormData()
formData.append('file', file) // file 必须是 File / Blob 对象
formData.append('fileType', 'PDF') // PDF / IMAGE / OTHER
formData.append('sortOrder', 0)
// 关键: Content-Type 不要手动设
// 不传 headers, 让 browser 自动加 'multipart/form-data; boundary=----WebKitFormBoundary...'
http.post(`/admin/contract/scheme/${schemeId}/attachments`, formData)
// 或显式覆盖默认 application/json:
http.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' } // 让 axios 自动加 boundary
})
```
## 后端接口规约(不变)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | File (multipart) | ✅ | 文件本体 |
| `fileType` | String (form field) | 否 | `PDF` / `IMAGE` / `OTHER`,不传按扩展名推断 |
| `sortOrder` | Integer (form field) | 否 | 默认 0 |
## 验证 curl后端确认 OK 的请求)
```bash
curl -X POST "https://api.test.1814.love:9443/admin/contract/scheme/3/attachments" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@文明旅游倡议书.pdf;type=application/pdf" \
-F "fileType=PDF" \
-F "sortOrder=0"
```
返回:
```json
{"code":200,"data":{"attachmentId":"...","ossUrl":"...","fileSize":...},"success":true}
```
## 后端配套(已部署测试服)
| PR | 改动 |
|----|------|
| #1640 | multipart 上限 50MB;超限友好提示 `code=100100` |
| #1641 | generic 500 错误暴露 exception class本次定位就靠这个 |
@mmg 看下 hl-ui 里调 `/admin/contract/scheme/.../attachments` 的代码,把对象传参改成 FormData 即可。