FastAPI 表单数据处理详解
在 Web 开发中,表单(Form)是最传统也是使用最广泛的客户端提交数据方式。无论是登录注册、搜索筛选,还是传统的 HTML 页面提交,表单数据都无处不在。FastAPI 通过 Form 提供了对 application/x-www-form-urlencoded 和 multipart/form-data 两种表单编码的原生支持。本文带你系统掌握 FastAPI 中的表单处理技巧。
基础表单字段
FastAPI 使用 Form 来声明表单字段,用法与 Query 和 Path 非常相似:
1 | from fastapi import FastAPI, Form |
几个关键点:
Form(...)中的...(Ellipsis)表示该字段是必填的,等同于Form()不带默认值。- 如果给默认值(如
Form("guest")),则字段变为可选。 - 表单字段支持
min_length、max_length、regex等校验参数,和Query、Path完全一致。 - 使用
Form时需要在路由装饰器中不要指定response_model为 Pydantic 模型时混合使用 body 参数,否则会冲突(下文详解)。
可选字段与默认值
对于可选字段,提供默认值即可:
1 |
|
这里用 str | None = Form(None) 表示该字段可以为空——客户端不传该字段时,值为 None。
多选值与列表字段
HTML 表单中常有 select multiple 或同名的 checkbox 组,传入的是一个值列表。FastAPI 通过 typing.List 来接收:
1 | from typing import List |
客户端发送 topics=python&topics=fastapi&topics=ai 时,服务端收到的 topics 就是 ["python", "fastapi", "ai"]。
文件与表单混合上传
最常见的高级场景是同时上传文件和其他表单字段(如用户头像 + 昵称)。此时需要用到 File 和 Form 的组合:
1 | from fastapi import FastAPI, File, Form, UploadFile |
注意: 当路由中同时存在 Form 和 File 参数时,请求的 Content-Type 自动变为 multipart/form-data。如果只有 Form 参数,默认使用 application/x-www-form-urlencoded。
表单数据与 Pydantic 模型
你可能会想把 Form 字段用 Pydantic 模型封装起来。但需要注意的是,表单数据和 JSON 请求体的处理路径不同——Pydantic 默认从 JSON body 读取数据,而 Form 从表单字段读取,二者不可直接互换。
推荐的模式是用一个独立的依赖函数来组织表单字段:
1 | from fastapi import Depends |
这样表单字段的定义被封装在 RegistrationForm 类中,路由函数通过 Depends() 获取实例,代码清晰且可复用。
手动处理表单的原始请求
在某些特殊场景下(如需要访问表单的键值对而不预先定义结构),可以直接从 Request 对象获取原始表单数据:
1 | from fastapi import Request |
request.form() 返回一个 FormData 对象(类似字典),可以迭代所有字段,每个值都是 FormData 的 str 或 UploadFile 类型。
表单编码的类型选择
FastAPI 根据路由参数自动推断合适的 Content-Type:
| 路由参数类型 | 自动 Content-Type |
|---|---|
仅有 Form |
application/x-www-form-urlencoded |
有 File / UploadFile |
multipart/form-data |
Form + File 混合 |
multipart/form-data |
对于简单的文本字段(如登录表单),x-www-form-urlencoded 足够且开销更小;涉及文件上传或大量数据时,multipart/form-data 是唯一选择。
小结
FastAPI 的 Form 让表单数据处理和查询参数一样直观——声明式字段定义、内建校验、自动文档生成。总结几个要点:
- 用
Form(...)声明必填字段,Form(default)声明可选字段。 - 用
List[str]类型注解接收同名多值字段。 Form+File混合时走multipart/form-data,仅Form时走x-www-form-urlencoded。- 表单字段可用
Depends+ 类封装实现结构化组织。 - 可通过
request.form()获取原始表单数据做自由解析。
掌握这些,你就能从容应对各种表单提交场景了。