在发送一些简单的
formData
时,我在使用 Fetch API JavaScript 方法时遇到一些问题,如下所示:
function register() {
var formData = new FormData();
var textInputName = document.getElementById('textInputName');
var sexButtonActive = document.querySelector('#buttonsMW > .btn.active');
var imagesInput = document.getElementById('imagesInput');
formData.append('name', textInputName.value);
if (sexButtonActive != null){
formData.append('sex', sexButtonActive.html())
} else {
formData.append('sex', "");
}
formData.append('images', imagesInput.files[0]);
fetch('/user/register', {
method: 'POST',
data: formData,
})
.then(response => response.json());
}
document.querySelector("form").addEventListener("submit", register);
在服务器端(FastAPI):
@app.post("/user/register", status_code=201)
def register_user(name: str = Form(...), sex: str = Form(...), images: List[UploadFile] = Form(...)):
try:
print(name)
print(sex)
print(images)
return "OK"
except Exception as err:
print(err)
print(traceback.format_exc())
return "Error"
单击提交按钮后,我得到
Error 422: Unprocessable entity
。因此,如果我尝试添加标题 Content-Type: multipart/form-data
,它也无济于事,因为我会得到另一个 Error 400: Bad Request
。我想了解我做错了什么,以及如何处理formData
而不出现此类错误?
422
错误响应正文将包含有关缺少哪个字段或与预期格式不匹配的字段的错误消息。由于您尚未提供(请这样做),我的猜测是由于您在端点中定义 images
参数的方式而触发了该错误。由于 images
预计是 List
的 File
,因此您应该使用 File
类型而不是 Form
来定义它。例如:
images: List[UploadFile] = File(...)
^^^^
使用
UploadFile
时,您不必在参数的默认值中使用File()
,这意味着以下内容也应该有效:
images: List[UploadFile]
因此,FastAPI 端点应类似于以下内容:
@app.post("/user/register")
async def register_user(name: str = Form(...), images: List[UploadFile] = File(...)):
pass
在前端,请确保在
body
函数中使用 data
(不是
fetch()
)参数来传递 FormData
对象(请参阅 MDN Web Docs 中的示例)。例如:
var nameInput = document.getElementById('nameInput');
var imagesInput = document.getElementById('imagesInput');
var formData = new FormData();
formData.append('name', nameInput.value);
for (const file of imagesInput.files)
formData.append('images', file);
fetch('/user/register', {
method: 'POST',
body: formData,
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});
请查看这个答案以及这个答案,它们提供了如何在前端使用 Fetch API 将多个
files
和 form
数据上传到 FastAPI 后端的工作示例。
至于在发送
Content-Type
时手动指定 multipart/form-data
,你不必必须(并且不应该)这样做,而是让浏览器设置 Content-Type
——请看一下这个答案了解更多详情。
所以,我发现这部分代码有错误:
formData.append('images', imagesInput.files[0]);
上传多个文件的正确方法是:
for (const image of imagesInput.files) {
formData.append('images', image);
}
此外,我们应该在FastAPI方法参数images: List[UploadFile] = File(...)
中使用
File(而不是Form),并在JS方法中将data更改为body。这不是一个错误,因为调用方法后,我们得到了正确类型的数据,例如:
Name: Bob
Sex: Man
Images: [<starlette.datastructures.UploadFile object at 0x7fe07abf04f0>]