一、Form 表单:不只 JSON 一种选择

在 FastAPI 中,Form 专门用来处理传统 HTML 表单提交的数据

from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
    return {"username": username}

必知要点:

  • 必须显式使用 Form(...),否则 FastAPI 会将参数当成查询参数或 JSON 请求体参数
  • 必须先安装 python-multipart:pip install python-multipart
  • 一个路径操作中可以声明多个 Form 参数,但不能同时声明要接收 JSON 的 Body 字段,这是 HTTP 协议的限制

表单模型(进阶封装)
当表单字段很多时(比如注册场景),可以用 Pydantic 模型进行结构化封装:

from pydantic import BaseModel

class RegisterForm(BaseModel):
    username: str = Form(...)
    email: str = Form(...)
    password: str = Form(...)
    confirm_password: str = Form(...)

@app.post("/register")
async def register_user(form: Annotated[RegisterForm, Form()]):
    if form.password != form.confirm_password:
        return {"error": "Passwords do not match"}
    return {"message": f"User {form.username} registered successfully"}

二、文件上传:UploadFile vs bytes

FastAPI 提供了两种接收文件的方式,选择哪种直接决定了性能和安全性。

2.1 bytes:小文件可用,但有风险

把文件全部读入内存,只适合几 KB 的头像或配置文件。大文件会导致内存暴涨。

2.2 UploadFile:生产环境标配

这是更推荐的异步方式。文件会分块处理,大文件自动写入磁盘临时文件,不会撑爆内存。

单文件上传示例:

from fastapi import FastAPI, File, UploadFile
import aiofiles

@app.post("/upload/")
async def upload_file(file: UploadFile = File(...)):
    # 异步保存到本地
    async with aiofiles.open(f"uploads/{file.filename}", "wb") as out_file:
        content = await file.read()
        await out_file.write(content)
    
    return {"filename": file.filename, "message": "上传成功"}

UploadFile 核心属性与方法:

· 属性:file.filename(文件名)、file.content_type(如 image/jpeg)、file.file(类文件对象)
· 异步方法:await file.read(size)、await file.write(data)、await file.seek(offset)、await file.close()

多文件上传:

from typing import List

@app.post("/upload-multiple/")
async def upload_multiple(files: List[UploadFile] = File(...)):
    for file in files:
        # 处理每个文件
        contents = await file.read()
        print(f"Received {file.filename}, size: {len(contents)}")
    return {"file_count": len(files)}

2.3 同时接收文件与表单数据

这是常见需求——比如上传图片的同时提交用户ID。FastAPI 中同时使用 File 和 Form 即可:

@app.post("/files/")
async def create_file(
    file: UploadFile = File(...),
    token: str = Form(...)
):
    return {
        "file_name": file.filename,
        "token": token,
        "file_content_type": file.content_type
    }

三、Cookie:客户端的“会员卡”

Cookie 是服务器存储在浏览器的小段数据,每次请求浏览器都会自动带上它。常见用途是会话管理、用户偏好设置等。

3.1 读取 Cookie

FastAPI 用 Cookie() 函数直接注入指定 cookie 值:

from fastapi import Cookie

@app.get("/items/")
async def read_items(ads_id: str | None = Cookie(default=None)):
    return {"ads_id": ads_id}

Cookie 也可以用 Pydantic 模型进行结构化校验(FastAPI 0.115.0+):

from pydantic import BaseModel

class Cookies(BaseModel):
    session_id: str
    fatebook_tracker: str | None = None

@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
    return cookies

3.2 设置 Cookie

设置 Cookie 必须通过 Response 对象,直接在返回的字典里设置是无效的。

登录后设置 Cookie 的完整示例:

from fastapi.responses import JSONResponse

@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
    # 验证密码逻辑略...
    
    response = JSONResponse(content={"message": "登录成功"})
    response.set_cookie(
        key="session_id",
        value="abc123xyz",
        max_age=3600,       # 有效期1小时(秒)
        httponly=True,      # 禁止JS读取,防XSS攻击
        secure=True,        # 仅HTTPS传输
        samesite="lax"      # CSRF保护
    )
    return response

set_cookie 关键参数说明:

参数作用
keyCookie名称
valueCookie值
max_age有效期(秒)
expires到期日期(datetime)
path生效路径
domain生效域名
secure设为True时仅HTTP发送
httponly设为True时禁止JavaScript访问
samesiteCSRF防护:lax,strict或none

四、常见陷阱与最佳实践

  1. 避免 JSON 与 Form 混用:一个接口不能同时要求 JSON 请求体和表单数据,FastAPI 按 Content-Type 决定解析方式,这不是框架限制,是 HTTP 协议本身的规定。
  2. 异步别忘了 await:UploadFile 的 read()、write() 等方法都是异步的,漏写 await 不会报语法错,但会导致文件内容读取为协程对象而非实际数据。
  3. Cookie 在 Swagger 文档中无法调试:Swagger UI 依赖 JS 发送请求,浏览器出于安全限制不允许 JS 随意操作 Cookie,所以在 /docs 里测试 Cookie 接口会报错,可以用 Postman 或 curl 测试。
  4. 文件上传记得保存:很多新手只调了 file.read() 读取内容,却没有写入磁盘。请求结束后内存数据就没了,必须用 shutil.copyfileobj 或 aiofiles 写到指定路径。
  5.  路径尾部斜杠问题:FastAPI 的路径匹配默认有“尾部斜杠重定向”机制,建议在路由定义时就统一加 / 或不加,避免意外重定向导致 Form 数据丢失。

学习 FastAPI 处理 Form、文件上传与 Cookie 后,最大的体会是:别用处理 JSON 的思路套在所有场景上。表单必须用 Form() 声明并安装 python-multipart;大文件首选 UploadFile 异步分块,避免内存溢出;设置 Cookie 必须通过响应对象操作,留意 httponly 和 secure 等安全属性。理解 HTTP 协议层面的差异,比学会框架 API 更重要。

下期预告:MySQL

作者:不爱编程的同学127      日期:2026.4.28

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐