You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FastAPI中结合Pydantic BaseModel上传文件与字典列表?

FastAPI接口接收文件与复杂参数时返回422错误

我编写了如下FastAPI代码,意图同时接收上传文件和包含数组、对象的结构化参数,但无论是通过交互式API文档测试,还是使用curl命令调用,都返回422 Unprocessable Entity错误。

代码示例

from fastapi import File, UploadFile, Request, FastAPI, Depends
from typing import List
from fastapi.responses import HTMLResponse
from pydantic import BaseModel, Field
from typing import Optional

app = FastAPI()

class BaseBox(BaseModel):
    l: float=Field(...)
    t: float=Field(...)
    r: float=Field(...)
    b: float=Field(...)

class BaseInput(BaseModel):
    boxes: List[BaseBox] = Field(...)
    words: List[str] = Field(...)
    width: Optional[float] = Field(...)
    height: Optional[float] = Field(...)

@app.post("/submit")
def submit(
    base_input: BaseInput = Depends(),
    file: UploadFile = File(...),
):
    return {
        "JSON Payload": base_input,
        "Filename": file.filename,
    }

@app.get("/")
def main(request: Request):
    return {"status":"alive"}

尝试的curl命令

curl -X 'POST' \
  'http://localhost:8007/submit?width=10&height=10' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@test.png;type=image/png' \
  -F 'boxes={
  \"l\": 0,
  \"t\": 0,
  \"r\": 0,
  \"b\": 0
}' \
  -F 'words=test,test2,tes3,test'

收到的错误

"POST /submit?width=10&height=10 HTTP/1.1" 422 Unprocessable Entity

解决方案

问题核心:当请求为multipart/form-data类型时,Depends()默认会尝试从JSON请求体解析BaseInput,但此时请求内容是表单格式,无法正确识别复杂的嵌套数组和对象结构,导致参数解析失败。以下两种方法可以解决:

方法一:通过表单字段传递JSON字符串并手动解析

修改接口代码,将结构化参数通过单个表单字段传递JSON字符串,再解析为BaseInput对象:

from fastapi import File, UploadFile, Request, FastAPI, Form
from typing import List
from pydantic import BaseModel, Field
from typing import Optional
import json

app = FastAPI()

class BaseBox(BaseModel):
    l: float=Field(...)
    t: float=Field(...)
    r: float=Field(...)
    b: float=Field(...)

class BaseInput(BaseModel):
    boxes: List[BaseBox] = Field(...)
    words: List[str] = Field(...)
    width: Optional[float] = None
    height: Optional[float] = None

@app.post("/submit")
def submit(
    base_input_json: str = Form(...),
    file: UploadFile = File(...),
    width: Optional[float] = None,
    height: Optional[float] = None
):
    # 解析JSON字符串为BaseInput实例
    base_input = BaseInput(**json.loads(base_input_json))
    # 覆盖查询参数中的宽高值(如果存在)
    if width is not None:
        base_input.width = width
    if height is not None:
        base_input.height = height
    return {
        "JSON Payload": base_input.dict(),
        "Filename": file.filename,
    }

对应的curl调用命令:

curl -X 'POST' \
  'http://localhost:8007/submit?width=10&height=10' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@test.png;type=image/png' \
  -F 'base_input_json={
    "boxes": [{"l": 0, "t": 0, "r": 0, "b": 0}],
    "words": ["test", "test2", "tes3", "test"]
  }'

方法二:使用Body(embed=True)解析表单中的复杂模型(FastAPI 0.95+)

如果使用FastAPI 0.95及以上版本,可以直接用Body(embed=True)让框架自动从表单字段中解析嵌套模型:

from fastapi import File, UploadFile, Request, FastAPI, Body
from typing import List
from pydantic import BaseModel, Field
from typing import Optional

app = FastAPI()

class BaseBox(BaseModel):
    l: float=Field(...)
    t: float=Field(...)
    r: float=Field(...)
    b: float=Field(...)

class BaseInput(BaseModel):
    boxes: List[BaseBox] = Field(...)
    words: List[str] = Field(...)
    width: Optional[float] = None
    height: Optional[float] = None

@app.post("/submit")
def submit(
    base_input: BaseInput = Body(..., embed=True),
    file: UploadFile = File(...)
):
    return {
        "JSON Payload": base_input.dict(),
        "Filename": file.filename,
    }

可以用两种方式调用:

  1. 拆分表单字段传递参数:
curl -X 'POST' \
  'http://localhost:8007/submit' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@test.png;type=image/png' \
  -F 'boxes[0][l]=0' \
  -F 'boxes[0][t]=0' \
  -F 'boxes[0][r]=0' \
  -F 'boxes[0][b]=0' \
  -F 'words=test' \
  -F 'words=test2' \
  -F 'words=tes3' \
  -F 'words=test' \
  -F 'width=10' \
  -F 'height=10'
  1. 单个表单字段传递完整JSON:
curl -X 'POST' \
  'http://localhost:8007/submit' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@test.png;type=image/png' \
  -F 'base_input={
    "boxes": [{"l": 0, "t": 0, "r": 0, "b": 0}],
    "words": ["test", "test2", "tes3", "test"],
    "width": 10,
    "height": 10
  }'

内容的提问来源于stack exchange,提问作者Thanh Long Phan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:50:57