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

FastAPI多表单请求报422错误:同时传dict与UploadFile失败

问题:FastAPI同时接收表单字典与上传文件时触发422错误

现象

  • 仅保留dict参数(移除UploadFile):接口正常接收参数
  • 仅保留UploadFile(移除dict和Body(...)):接口正常接收上传文件
  • 同时使用dict与UploadFile:多表单请求触发422 Unprocessable Entity错误

Swagger测试请求示例

------WebKitFormBoundaryewMFgxOJYb8xJ70T
Content-Disposition: form-data; name="media"

{
  "media_title": "string",
  "media_description": "string",
  "media_type": "string",
  "media_tags": "tag1",
  "firm_id": 0,
  "fund_id": 0,
  "user_id": 0,
  "file_path": "string",
  "version": 0
}
------WebKitFormBoundaryewMFgxOJYb8xJ70T
Content-Disposition: form-data; name="media_file"; filename="file_example_MP4_640_3MG.mp4"
Content-Type: video/mp4


------WebKitFormBoundaryewMFgxOJYb8xJ70T--

现有代码

class Media(BaseModel):
    media_title: str
    media_description: str
    media_type: str
    media_tags: str
    firm_id: int
    fund_id: int = None
    user_id: int
    file_path: str
    version: int

@media_router.post("/upload", response_model=Media)
async def upload_media(db: db_dependency, media: dict = Body(...), media_file: UploadFile = File(...)):
    try:
        media["file_path"] = save_uploaded_file(media_file)
        output = await dblibrary.create(db=db, obj_in=Media(**media))
        return output
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

错误信息

{
  "detail": [
    {
      "type": "dict_type",
      "loc": [
        "body",
        "media"
      ],
      "msg": "输入应为有效的字典",
      "input": "{
  \"media_title\": \"string\",
  \"media_description\": \"string\",
  \"media_type\": \"string\",
  \"media_tags\": \"tag1\",
  \"firm_id\": 0,
  \"fund_id\": 0,
  \"user_id\": 0,
  \"file_path\": \"string\",
  \"version\": 0
}",
      "url": "https://errors.pydantic.dev/2.6/v/dict_type"
    }
  ]
}

解决方案

问题本质:当接口同时存在File和Body参数时,FastAPI会将请求解析为multipart/form-data格式,但Body(...)定义的dict参数默认期望从请求体的JSON中获取,而非表单字段里的字符串格式JSON,导致解析失败。

方法1:表单接收JSON字符串后手动转字典

将media参数改为Form接收,用json.loads解析成字典后再传入模型:

import json
from fastapi import Form

@media_router.post("/upload", response_model=Media)
async def upload_media(
    db: db_dependency,
    media: str = Form(...),
    media_file: UploadFile = File(...)
):
    try:
        media_dict = json.loads(media)
        media_dict["file_path"] = save_uploaded_file(media_file)
        output = await dblibrary.create(db=db, obj_in=Media(**media_dict))
        return output
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

方法2:用Pydantic模型直接验证JSON字符串

利用Media模型的model_validate_json方法,直接解析表单中的JSON字符串:

from fastapi import Form

@media_router.post("/upload", response_model=Media)
async def upload_media(
    db: db_dependency,
    media: str = Form(...),
    media_file: UploadFile = File(...)
):
    try:
        media_obj = Media.model_validate_json(media)
        media_obj.file_path = save_uploaded_file(media_file)
        output = await dblibrary.create(db=db, obj_in=media_obj)
        return output
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

方法3:拆分模型字段为单独表单参数(可选)

如果不需要传递完整JSON对象,可将Media的每个字段设为单独的Form参数,结合UploadFile使用:

@media_router.post("/upload", response_model=Media)
async def upload_media(
    db: db_dependency,
    media_title: str = Form(...),
    media_description: str = Form(...),
    media_type: str = Form(...),
    media_tags: str = Form(...),
    firm_id: int = Form(...),
    fund_id: int = Form(None),
    user_id: int = Form(...),
    version: int = Form(...),
    media_file: UploadFile = File(...)
):
    try:
        media_dict = {
            "media_title": media_title,
            "media_description": media_description,
            "media_type": media_type,
            "media_tags": media_tags,
            "firm_id": firm_id,
            "fund_id": fund_id,
            "user_id": user_id,
            "file_path": save_uploaded_file(media_file),
            "version": version
        }
        output = await dblibrary.create(db=db, obj_in=Media(**media_dict))
        return output
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

内容的提问来源于stack exchange,提问作者user3910623

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 10:35:13