0

0

FastAPI高级用法:如何同时上传文件与Pydantic列表字典数据

花韻仙語

花韻仙語

发布时间:2025-09-19 14:02:01

|

442人浏览过

|

来源于php中文网

原创

FastAPI高级用法:如何同时上传文件与Pydantic列表字典数据

本教程深入探讨了在FastAPI中同时上传文件和Pydantic复杂数据结构(如字典列表)的挑战与解决方案。文章首先剖析了传统方法中遇到的HTTP协议限制和Pydantic模型定义问题,随后详细介绍了两种核心策略:通过Form参数传输JSON字符串并手动解析,以及利用Pydantic的model_validator自动处理Body中的JSON字符串。通过实际代码示例,本教程旨在帮助开发者高效地构建支持混合数据上传的FastAPI接口。

1. 挑战:同时上传文件与复杂JSON数据

在fastapi应用开发中,我们经常需要处理多种类型的请求数据,例如文件上传(uploadfile)和结构化的json数据(pydantic basemodel)。当这些数据类型需要同时在一个请求中提交时,开发者可能会遇到一些挑战,特别是当json数据包含列表(list)或字典列表(list[basemodel])时。

常见的错误尝试包括:

  • 将Pydantic模型直接作为依赖项(Depends())与UploadFile一起使用,期望它能自动解析JSON体。
  • 在Pydantic模型中定义List类型的字段作为查询参数,但未显式使用Query()。
  • 尝试通过multipart/form-data同时发送JSON数据和文件。

这些尝试通常会导致422 Unprocessable Entity错误,其根本原因在于HTTP协议对请求体编码的限制以及FastAPI/Pydantic对不同数据源的解析机制。

核心问题点:

  1. HTTP协议限制: HTTP协议通常不允许在一个请求体中同时使用multipart/form-data(用于文件上传)和application/json(用于JSON数据)两种编码类型。当接口定义中包含File()参数时,FastAPI会将整个请求体视为multipart/form-data。
  2. List类型查询参数: 当Pydantic模型中包含List[str]或List[int]等列表类型的查询参数时,必须显式使用Field(Query(...))进行声明,否则FastAPI无法正确解析。
  3. List[dict]作为查询参数: List[BaseModel](即字典列表)无法作为查询参数传递。Pydantic模型中的复杂对象列表通常期望作为请求体的一部分。

2. Pydantic模型中列表参数的正确声明

在深入探讨文件与JSON混合上传之前,我们首先需要理解如何在Pydantic模型中正确声明列表类型的查询参数。如果你的Pydantic模型字段是List[str]或List[float]等,你需要使用Query()将其包装在Field()中。

示例代码 1:Pydantic模型中列表查询参数的正确用法

from fastapi import FastAPI, Query, Depends
from pydantic import BaseModel, Field
from typing import Optional, List

app = FastAPI()

class BaseQueryParams(BaseModel):
    width: Optional[float] = Field(None, description="宽度")
    height: Optional[float] = Field(None, description="高度")
    words: List[str] = Field(Query(..., description="单词列表")) # 必须使用 Query(...)

@app.get("/query-example")
async def get_with_list_query(params: BaseQueryParams = Depends()):
    """
    一个演示如何使用列表查询参数的端点。
    示例请求: /query-example?width=10.5&words=apple&words=banana
    """
    return params

说明:

  • words: List[str] = Field(Query(...)) 明确告诉FastAPI words 是一个列表类型的查询参数,可以接收多个同名参数值(例如 ?words=a&words=b)。
  • 如果缺少Query(...),FastAPI将无法正确解析List类型的查询参数。

3. 核心解决方案:同时上传文件与复杂JSON数据

由于HTTP协议的限制,我们不能直接将Pydantic模型(作为application/json)和文件(作为multipart/form-data)同时发送。解决方案的关键在于:将Pydantic模型的数据编码成一个字符串,并通过multipart/form-data的一部分(例如一个Form字段)发送,然后在服务器端进行解析。

以下介绍两种常用的实现方法。

3.1 方法一:通过Form参数传递JSON字符串并手动解析

这种方法将Pydantic模型的数据序列化为JSON字符串,然后作为Form参数的一部分提交。服务器端通过一个依赖函数手动解析这个JSON字符串。

示例代码 2:使用Form参数和依赖函数解析JSON数据

app.py

GitHub Copilot
GitHub Copilot

GitHub AI编程工具,实时编程建议

下载
from fastapi import FastAPI, status, Form, UploadFile, File, Depends, Query
from pydantic import BaseModel, Field, ValidationError
from fastapi.exceptions import HTTPException
from fastapi.encoders import jsonable_encoder
from typing import Optional, List
import json # 导入 json 模块

app = FastAPI()

# 定义查询参数模型
class BaseQueryParams(BaseModel):
    width: Optional[float] = Field(None, description="宽度")
    height: Optional[float] = Field(None, description="高度")
    words: List[str] = Field(Query(..., description="单词列表")) # 列表查询参数

# 定义复杂JSON数据模型中的子模型
class BaseBox(BaseModel):
    l: float = Field(..., description="左坐标")
    t: float = Field(..., description="上坐标")
    r: float = Field(..., description="右坐标")
    b: float = Field(..., description="下坐标")

# 定义复杂JSON数据模型
class BasePayload(BaseModel):
    boxes: List[BaseBox] = Field(..., description="边界框列表")
    comments: List[str] = Field(..., description="评论列表")
    code: int = Field(..., description="状态码")

# 定义一个依赖函数,用于解析 Form 参数中的 JSON 字符串
def parse_json_form_data(data: str = Form(...)):
    try:
        # 尝试将 Form 参数中的字符串解析为 BasePayload 模型
        return BasePayload.model_validate_json(data)
    except ValidationError as e:
        # 如果解析失败,抛出 422 错误
        raise HTTPException(
            detail=jsonable_encoder(e.errors()),
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        )

@app.post("/submit_form_json")
def submit_with_form_json(
    query_params: BaseQueryParams = Depends(), # 查询参数
    payload: BasePayload = Depends(parse_json_form_data), # JSON数据通过Form解析
    files: List[UploadFile] = File(...), # 文件列表
):
    """
    通过 Form 参数传递 JSON 字符串,并同时上传文件。
    """
    return {
        "Query Params": query_params,
        "JSON Payload": payload,
        "Filenames": [file.filename for file in files],
    }

客户端请求示例 (使用 curl):

假设你有一个名为 test.png 的文件。

curl -X 'POST' \
  'http://localhost:8000/submit_form_json?width=10.5&height=20.0&words=apple&words=banana' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'files=@test.png;type=image/png' \
  -F 'data={"boxes": [{"l": 0,"t": 0,"r": 10,"b": 10}], "comments": ["first comment", "second comment"], "code": 123}'

说明:

  • BaseQueryParams 用于处理 URL 中的查询参数,其中的 words 字段正确使用了 Query(...)。
  • BasePayload 定义了我们期望的复杂JSON数据结构。
  • parse_json_form_data 是一个关键的依赖函数。它接收一个 Form 参数 data(客户端将JSON字符串作为此字段发送),然后使用 BasePayload.model_validate_json() 尝试将其解析为 BasePayload 对象。
  • 如果JSON字符串格式不正确,ValidationError 会被捕获并转换为 HTTPException,返回 422 状态码和详细错误信息。
  • files: List[UploadFile] = File(...) 用于接收一个或多个文件。

3.2 方法二:利用Pydantic的model_validator自动解析Body中的JSON字符串

这种方法通过Pydantic模型自身的model_validator来处理从请求体中接收到的JSON字符串。它将JSON字符串视为一个特殊的输入格式,并在模型实例化之前进行解析。这种方式通常更简洁,并且在Swagger UI (/docs) 中能更好地展示请求体结构。

示例代码 3:使用model_validator解析Body中的JSON字符串

app.py

from fastapi import FastAPI, Body, UploadFile, File, Depends, Query
from pydantic import BaseModel, Field, model_validator
from typing import Optional, List
import json

app = FastAPI()

# 定义查询参数模型
class BaseQueryParams(BaseModel):
    width: Optional[float] = Field(None, description="宽度")
    height: Optional[float] = Field(None, description="高度")
    words: List[str] = Field(Query(..., description="单词列表")) # 列表查询参数

# 定义复杂JSON数据模型中的子模型
class BaseBox(BaseModel):
    l: float = Field(..., description="左坐标")
    t: float = Field(..., description="上坐标")
    r: float = Field(..., description="右坐标")
    b: float = Field(..., description="下坐标")

# 定义复杂JSON数据模型,并添加 model_validator
class BasePayload(BaseModel):
    boxes: List[BaseBox] = Field(..., description="边界框列表")
    comments: List[str] = Field(..., description="评论列表")
    code: int = Field(..., description="状态码")

    @model_validator(mode="before")
    @classmethod
    def validate_to_json(cls, value):
        """
        在模型验证之前,如果输入是字符串,尝试将其解析为JSON。
        这允许客户端将JSON数据作为字符串发送。
        """
        if isinstance(value, str):
            try:
                return cls(**json.loads(value))
            except json.JSONDecodeError as e:
                # 如果JSON解析失败,Pydantic会捕获并抛出ValidationError
                # 这里可以添加更具体的错误处理,或让Pydantic默认处理
                raise ValueError("Invalid JSON string for BasePayload") from e
        return value

@app.post("/submit_body_json")
def submit_with_body_json(
    query_params: BaseQueryParams = Depends(), # 查询参数
    payload: BasePayload = Body(...), # JSON数据通过Body参数传递
    files: List[UploadFile] = File(...), # 文件列表
):
    """
    通过 Body 参数传递 JSON 字符串(由 model_validator 处理),并同时上传文件。
    """
    return {
        "Query Params": query_params,
        "JSON Payload": payload,
        "Filenames": [file.filename for file in files],
    }

客户端请求示例 (使用 curl):

假设你有一个名为 test.png 的文件。

curl -X 'POST' \
  'http://localhost:8000/submit_body_json?width=10.5&height=20.0&words=apple&words=banana' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'files=@test.png;type=image/png' \
  -F 'payload={"boxes": [{"l": 0,"t": 0,"r": 10,"b": 10}], "comments": ["first comment", "second comment"], "code": 123}'

说明:

  • BasePayload 模型中新增了一个 model_validator(mode="before") 方法。
  • validate_to_json 方法在Pydantic模型验证之前被调用。如果 value 是一个字符串(即客户端发送的JSON字符串),它会尝试使用 json.loads() 将其解析为字典,然后用这个字典来实例化 BasePayload。
  • payload: BasePayload = Body(...) 声明 payload 是请求体的一部分。FastAPI会将其作为 multipart/form-data 中的一个字段来处理,而 model_validator 则负责将其从字符串解析为Pydantic对象。
  • 这种方法在FastAPI的/docs接口中显示更友好,因为它能自动生成 BasePayload 的示例输入结构。

4. 注意事项与总结

  • 选择合适的方法: 方法二(使用model_validator)通常更推荐,因为它将JSON解析逻辑封装在Pydantic模型内部,使代码更简洁,且与FastAPI的文档生成集成度更高。
  • 客户端请求格式: 无论选择哪种方法,客户端都需要将Pydantic模型的数据序列化为JSON字符串,并作为multipart/form-data中的一个字段发送。
  • 查询参数: 对于URL中的列表类型查询参数,务必使用 Field(Query(...)) 进行声明。
  • 错误处理: 两种方法都包含了对JSON解析失败的错误处理,确保API在接收到无效数据时能返回清晰的错误信息。
  • 多文件上传: 示例中使用了 files: List[UploadFile] = File(...) 来支持多文件上传。如果只需要上传单个文件,可以将其改为 file: UploadFile = File(...)。

通过上述两种方法,开发者可以有效地解决在FastAPI中同时上传文件和复杂Pydantic模型数据(特别是包含字典列表)的挑战,构建出功能强大且健壮的API接口。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

418

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

535

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

311

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

77

2025.09.10

Python FastAPI异步API开发_Python怎么用FastAPI构建异步API
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API

Python FastAPI 异步开发利用 async/await 关键字,通过定义异步视图函数、使用异步数据库库 (如 databases)、异步 HTTP 客户端 (如 httpx),并结合后台任务队列(如 Celery)和异步依赖项,实现高效的 I/O 密集型 API,显著提升吞吐量和响应速度,尤其适用于处理数据库查询、网络请求等耗时操作,无需阻塞主线程。

27

2025.12.22

数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

309

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

578

2024.04.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.5万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号