如何在FastAPI的POST端点中上传文件并传递字典列表参数?
问题:FastAPI POST端点同时上传多文件+字典列表参数报错
场景
需要在同一个FastAPI的POST接口中同时上传多个文件,并且传递字典列表格式的参数,测试代码如下:
from fastapi import FastAPI, File, UploadFile, Body, status from pydantic import BaseModel, model_validator from typing import Optional, List import json app = FastAPI() class FileUpload(BaseModel): name: str header_exists: bool rows_to_skip: int class ListFileUpload(BaseModel): data: List[FileUpload] @app.post("/upload") async def file_upload(files: List[UploadFile], params: ListFileUpload): for file in files: print(file) print(params)
报错信息(翻译后)
422 不可处理实体
字段params验证失败:值不是有效的字典类型
原因
当接口同时接收文件和复杂结构参数时,请求会以multipart/form-data格式发送,但FastAPI默认无法直接从表单数据中解析嵌套的Pydantic模型(比如包含列表的ListFileUpload),需要显式指定参数的解析方式。
解决方案
有两种可行的处理方式:
方式1:用Body(...)显式标记参数,指定媒体类型
修改接口定义,给params参数加上Body(...),让FastAPI从请求体的表单字段中解析JSON结构:
@app.post("/upload") async def file_upload( files: List[UploadFile] = File(...), params: ListFileUpload = Body(..., media_type="application/json") ): for file in files: print(f"文件名: {file.filename}") print(f"参数内容: {params.dict()}") return {"files_count": len(files), "params": params.dict()}
方式2:将参数序列化为JSON字符串传递,后端手动解析
如果客户端不方便发送JSON格式的表单字段,可让客户端把参数序列化成JSON字符串,后端手动解析为Pydantic模型:
@app.post("/upload") async def file_upload( files: List[UploadFile] = File(...), params_str: str = Body(...) ): params = ListFileUpload.parse_raw(params_str) for file in files: print(f"文件名: {file.filename}") print(f"参数内容: {params.dict()}") return {"files_count": len(files), "params": params.dict()}
测试示例(curl命令)
方式1测试命令:
curl -X POST "http://localhost:8000/upload" \ -H "Content-Type: multipart/form-data" \ -F "files=@file1.csv" \ -F "files=@file2.csv" \ -F 'params={"data": [{"name": "file1", "header_exists": true, "rows_to_skip": 1}, {"name": "file2", "header_exists": false, "rows_to_skip": 0}]}'
方式2测试命令:
curl -X POST "http://localhost:8000/upload" \ -H "Content-Type: multipart/form-data" \ -F "files=@file1.csv" \ -F "files=@file2.csv" \ -F 'params_str="{\"data\": [{\"name\": \"file1\", \"header_exists\": true, \"rows_to_skip\": 1}, {\"name\": \"file2\", \"header_exists\": false, \"rows_to_skip\": 0}]}"'
内容的提问来源于stack exchange,提问作者Tanmay Tete
相关产品推荐
相关产品推荐

