模型,请求数据
- 使用记录
- 模型
- 响应模型
- 减少代码量
- 任意 `dict` 构成的响应
 
- 请求附加信息
- Header信息
 
- 其他的请求信息
- 表单数据
- 文件数据
- 基本使用
- 多文件
 
- 表单+文件
 
 
使用记录
模型
响应模型
有的时候一个post接口,请求模型和响应模型我们需要的字段是不一样的,比如用户登录的接口:
from typing import Anyfrom fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: str | None = Noneclass UserOut(BaseModel):username: stremail: EmailStrfull_name: str | None = None@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:return user
上述代码中添加了两个模型,UserIn用来做请求体的映射,而UserOut则是返回值的映射,通过response_model即可指定返回值模型;
除此之外,还可以选择在返回的时候忽略空的值,否则可能出现一个接口的返回值很冗长,携带大量空的json键值对,这个操作借由response_model_exclude_unset=True来实现。
减少代码量
对于用户模型来说,可以声明一个 UserBase 模型作为其他模型的基类。然后可以创建继承该模型属性(类型声明,校验等)的子类。这样,可以仅声明模型之间的差异部分(具有明文的 password、具有 hashed_password 以及不包括密码)。
from fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserBase(BaseModel):username: stremail: EmailStrfull_name: str | None = Noneclass UserIn(UserBase):password: strclass UserOut(UserBase):passclass UserInDB(UserBase):hashed_password: strdef fake_password_hasher(raw_password: str):return "supersecret" + raw_passworddef fake_save_user(user_in: UserIn):hashed_password = fake_password_hasher(user_in.password)user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)print("User saved! ..not really")return user_in_db@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):user_saved = fake_save_user(user_in)return user_saved
任意 dict 构成的响应
 
可以使用一个任意的普通 dict 声明响应,仅声明键和值的类型,而不使用 Pydantic 模型。如果事先不知道有效的字段/属性名称(对于 Pydantic 模型是必需的),这将很有用。在这种情况下,可以使用 typing.Dict:
from fastapi import FastAPIapp = FastAPI()@app.get("/keyword-weights/", response_model=dict[str, float])
async def read_keyword_weights():return {"foo": 2.3, "bar": 3.4}
请求附加信息
定义Header参数和Cookies参数的方式与定义Query等参数的方式是一样的。
这是因为他们都是
Path,Query等类的兄弟类型。它也继承自通用的Param类.
Header信息
需要注意的其实只有一个点,那就是Header提供了自动转换的能力。
首先要知道大多数标准的headers用-分割,然而这种名字的变量在Python中无效,比如user-agent,因此,默认情况下,Header 将把参数名称的字符从_ 转换为-)来提取并记录 headers.
from typing import Annotatedfrom fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):return {"User-Agent": user_agent}
其他的请求信息
上面说的大部分都是json数据格式的交互,有的时候会提交表单数据,或者用户上传文件,这里有别的处理方式。
表单数据
使用表单首先要安装额外的包
pip install python-multipart
从 fastapi 导入 Form:
from fastapi import FastAPI, Formapp = FastAPI()@app.post("/login/")
async def login(username: str = Form(), password: str = Form()):return {"username": username}
Tips:例如,OAuth2 规范的 “密码流” 模式规定要通过表单字段发送
username和password。该规范要求字段必须命名为
username和password,并通过表单字段发送,不能用 JSON。使用
Form可以声明与Body(及Query、Path、Cookie)相同的元数据和验证。声明表单体要显式使用
Form,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数
文件数据
基本使用
文件数据使用File,从 fastapi 导入 File 并使用:
from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File()):return {"file_size": len(file)}上面的例子中如果把路径操作函数参数的类型声明为 bytes,FastAPI 将以 bytes 形式读取和接收文件内容。这种方式把文件的所有内容都存储在内存里,适用于小型文件,实际上在多数情况下UploadFile 更好用。
from fastapi import FastAPI, UploadFileapp = FastAPI()@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):return {"filename": file.filename}
UploadFile的优势:
- 使用spooled文件,存储在内存的文件超出最大上限时,FastAPI 会把文件存入磁盘;
- 这种方式更适于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存;
- 可获取上传文件的元数据;
- 自带 file-like async接口;
- 暴露的 Python SpooledTemporaryFile对象,可直接传递给其他预期「file-like」对象的库。
UploadFile 的属性如下:
- filename:上传文件名字符串(- str),例如,- myimage.jpg;
- content_type:内容类型(MIME 类型 / 媒体类型)字符串(- str),例如,- image/jpeg;
- file:- SpooledTemporaryFile( file-like 对象)。其实就是 Python文件,可直接传递给其他预期- file-like对象的函数或支持库。
UploadFile 支持以下 async 方法,(使用内部 SpooledTemporaryFile)可调用相应的文件方法。
- write(data):把- data(- str或- bytes)写入文件;
- read(size):按指定数量的字节或字符(- size(- int))读取文件内容;
- seek(offset):移动至文件- offset(int)字节处的位置;- 例如,await myfile.seek(0)移动到文件开头;
- 执行 await myfile.read()后,需再次读取已读取内容时,这种方法特别好用;
 
- 例如,
- close():关闭文件。
与 JSON 不同,HTML 表单(<form></form>)向服务器发送数据通常使用「特殊」的编码,在不包含文件时,表单数据一般采用 application/x-www-form-urlencoded「媒体类型」编码,包含文件时则使用multipart/form-data编码;
如果想要文件上传选项是可选的,只需要以None作为注解即可:
from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)): # 这里多了None = File(default=None)if not file:return {"message": "No file sent"}else:return {"file_size": len(file)}@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):if not file:return {"message": "No upload file sent"}else:return {"filename": file.filename}
多文件
同一个表单字段可以包含多个文件,可以想到这种情况下使用含 bytes 或 UploadFile 的List:
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponseapp = FastAPI()@app.post("/files/")
async def create_files(files: list[bytes] = File()):return {"file_sizes": [len(file) for file in files]}@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):return {"filenames": [file.filename for file in files]}@app.get("/")
async def main():content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>"""return HTMLResponse(content=content)
表单+文件
直接设置多个内容即可:
from fastapi import FastAPI, File, Form, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):return {"file_size": len(file),"token": token,"fileb_content_type": fileb.content_type,}