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

FastAPI上传文件与字典类型Form参数兼容及文档优化问题

FastAPI同时上传文件与Form字典字段的最优解决方案

问题场景

需要通过Form Data同时上传文件和存入数据库的凭证信息(要求为str类型键、Any类型值的字典),最初编写的接口如下:

@router.post('/upload', response_model=SuccessResponse)
async def upload(file: UploadFile = File(...), credential: Dict[str, Any] = Form(...)):
     return response.success(SuccessResponse(data=credential))

使用curl调用时:

curl --location '127.0.0.1:8000/api/v1/file/upload' \
--form 'file=@"/Users/hanie/Desktop/example.jpg"' \
--form 'credential[user_id]="2"' \
--form 'credential[page_id]="1"'

始终收到凭证字段缺失的错误:

{"detail": [
    {
        "type": "missing",
        "loc": [
            "body",
            "credential"
        ],
        "msg": "Field required",
        "input": null
    }
]}

尝试方案的缺陷

通过自定义依赖函数解析Form数据的方式可以正常运行,但会导致OpenAPI文档无法正确识别credential字段:

async def parse_dict_form(request: Request) -> Dict[str, Any]:
    form_data = await request.form()
    result = {}
    prefix_with_brackets = "credential["
    for key, value in form_data.items():
        if key.startswith(prefix_with_brackets) and key.endswith(']'):
            actual_key = key[len(prefix_with_brackets):-1]
            result[actual_key] = value
    return result

@router.post('/upload', response_model=SuccessResponse)
async def upload(
        file: UploadFile = File(...), 
        credential: Dict[str, Any] = Depends(parse_dict_form)
):
    return response.success(SuccessResponse(data=credential))

最优解决方法

方法1:使用JSON格式的Form字段(推荐)

这种方式既保证功能正常,又能让OpenAPI文档正确识别字段结构,是最简洁的方案。

接口代码(用Pydantic模型,文档更友好)

from pydantic import BaseModel
from fastapi import UploadFile, File, Form

# 定义凭证的Pydantic模型,明确字段结构
class Credential(BaseModel):
    user_id: str
    page_id: str
    # 可根据需求添加其他字段

@router.post('/upload', response_model=SuccessResponse)
async def upload(
    file: UploadFile = File(...),
    # 直接用Pydantic模型接收Form中的JSON字符串,自动解析为字典
    credential: Credential = Form(...)
):
    return response.success(SuccessResponse(data=credential.dict()))

对应的curl调用

传递JSON格式的credential字段:

curl --location '127.0.0.1:8000/api/v1/file/upload' \
--form 'file=@"/Users/hanie/Desktop/example.jpg"' \
--form 'credential="{\"user_id\":\"2\",\"page_id\":\"1\"}"'

如果不想定义Pydantic模型,也可以直接用Dict[str, Any]:

from typing import Dict, Any
from fastapi import UploadFile, File, Form

@router.post('/upload', response_model=SuccessResponse)
async def upload(
    file: UploadFile = File(...),
    credential: Dict[str, Any] = Form(...)
):
    return response.success(SuccessResponse(data=credential))

curl调用方式和上面一致,传递JSON字符串即可。

方法2:保留表单数组格式并修复OpenAPI文档

如果必须使用credential[user_id]这种表单字段格式,可以在自定义依赖中添加OpenAPI schema注释,让文档正确显示字段:

from fastapi import Depends, Request, UploadFile, File
from typing import Dict, Any

async def parse_dict_form(request: Request) -> Dict[str, Any]:
    form_data = await request.form()
    result = {}
    prefix_with_brackets = "credential["
    for key, value in form_data.items():
        if key.startswith(prefix_with_brackets) and key.endswith(']'):
            actual_key = key[len(prefix_with_brackets):-1]
            result[actual_key] = value
    return result

# 手动添加OpenAPI schema,让文档显示正确的表单字段
parse_dict_form.__openapi_operation__ = {
    "requestBody": {
        "content": {
            "multipart/form-data": {
                "schema": {
                    "type": "object",
                    "properties": {
                        "file": {"type": "string", "format": "binary"},
                        "credential[user_id]": {"type": "string"},
                        "credential[page_id]": {"type": "string"}
                    },
                    "required": ["file", "credential[user_id]", "credential[page_id]"]
                }
            }
        }
    }
}

@router.post('/upload', response_model=SuccessResponse)
async def upload(
        file: UploadFile = File(...), 
        credential: Dict[str, Any] = Depends(parse_dict_form)
):
    return response.success(SuccessResponse(data=credential))

这种方法需要手动维护OpenAPI schema,适合必须保留原有表单格式的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:26:20