文章
FastAPI官方文档学习02
1、Cookie
@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}
- ads_id参数 属于Cookie参数
- 类似于路径参数 Path(从路径中获取数据) 查询参数 Query(从查询参数中获取数据)
- Cookie()从Cookie中获取对应参数名的cookie 键的值
2、Header
@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):
return {"User-Agent": user_agent}
- user_agent参数 属于Header参数,从Header中获取对应参数名的Header的属性的值
- 默认情况下,Header 把参数名中的字符由下划线(_)改为连字符(-)来提取并存档请求头
- 如需禁用下划线自动转换为连字符,可以把 Header 的 convert_underscores 参数设置为 False
注意,使用 convert_underscores = False
要慎重,有些 HTTP 代理和服务器不支持使用带有下划线的请求头。
重复的请求头
例如,声明 X-Token 多次出现的请求头,可以写成这样:
@app.get("/items/")
async def read_items(x_token: Annotated[list[str] | None, Header()] = None):
return {"X-Token values": x_token}
3、Cookie参数模型
class Cookies(BaseModel):
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
return cookies
- FastAPI 将从请求中接收到的 cookie 中提取出每个字段的数据,并提供您定义的 Pydantic 模型。
4、Header参数模型
class CommonHeaders(BaseModel):
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: Annotated[CommonHeaders, Header()]):
return headers
5、响应模型
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]
# 请求体类型 和 响应体类型都是Item类型
@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
return item
FastAPI 将使用此 response_model
来:
- 将输出数据转换为其声明的类型。
- 校验数据。
重要的是:将输出数据限制在该模型定义内
添加输出模型
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user
举个例子,当你在 NoSQL 数据库中保存了具有许多可选属性的模型,但你又不想发送充满默认值的很长的 JSON 响应。
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
6、更多模型
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
class UserIn(UserBase):
password: str
class UserOut(UserBase):
pass
class UserInDB(UserBase):
hashed_password: str
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.model_dump(), 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)
print(user_saved)
return user_saved
响应可以声明为两种类型的 Union 类型,即该响应可以是两种类型中的任意类型。
class BaseItem(BaseModel):
description: str
type: str
class CarItem(BaseItem):
type: str = "car"
class PlaneItem(BaseItem):
type: str = "plane"
size: int
items = {
"item1": {"description": "All my friends drive a low rider", "type": "car"},
"item2": {
"description": "Music is my aeroplane, it's my aeroplane",
"type": "plane",
"size": 5,
},
}
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
return items[item_id]
模型列表
class Item(BaseModel):
name: str
description: str
items = [
{"name": "Foo", "description": "There comes my hero"},
{"name": "Red", "description": "It's my aeroplane"},
]
@app.get("/items/", response_model=list[Item])
async def read_items():
return items
任意 dict
构成的响应
任意的 dict 都能用于声明响应,只要声明键和值的类型,无需使用 Pydantic 模型。
@app.get("/keyword-weights/", response_model=dict[str, float])
async def read_keyword_weights():
return {"foo": 2.3, "bar": 3.4}
7、响应状态码
100
及以上的状态码用于返回信息。这类状态码很少直接使用。具有这些状态码的响应不能包含响应体200
及以上的状态码用于表示成功。这些状态码是最常用的200
是默认状态代码,表示一切正常201
表示已创建,通常在数据库中创建新记录后使用204
是一种特殊的例子,表示无内容。该响应在没有为客户端返回内容时使用,因此,该响应不能包含响应体
300
及以上的状态码用于重定向。具有这些状态码的响应不一定包含响应体,但304
未修改是个例外,该响应不得包含响应体400
及以上的状态码用于表示客户端错误。这些可能是第二常用的类型404
,用于未找到响应- 对于来自客户端的一般错误,可以只使用
400
500
及以上的状态码用于表示服务器端错误。几乎永远不会直接使用这些状态码。应用代码或服务器出现问题时,会自动返回这些状态代码
from fastapi import FastAPI, status
import fastapi_cdn_host
app = FastAPI()
fastapi_cdn_host.patch_docs(app)
@app.post("/items/", status_code=201)
async def create_item(name: str):
return {"name": name}
@app.post("/users/", status_code=status.HTTP_201_CREATED)
async def create_user(name: str):
return {"name": name}
8、表单数据
接收的不是 JSON,而是表单字段时,要使用 Form。
创建表单(Form)参数的方式与 Body 和 Query 一样:
from fastapi import FastAPI, Form
@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
return {"username": username}
- 使用 Form 可以声明与 Body (及 Query、Path、Cookie)相同的元数据和验证。
- Form 是直接继承自 Body 的类。
- 声明表单体要显式使用 Form ,否则,FastAPI 会把该参数当作查询参数或请求体(JSON)参数。
表单数据的「媒体类型」编码一般为 application/x-www-form-urlencoded。
但包含文件的表单编码为 multipart/form-data。
9、表单模型
前置依赖
pip install python-multipart
class FormData(BaseModel):
username: str
password: str
@app.post("/login")
async def login(data: Annotated[FormData, Form()]):
return data
10、请求文件
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
import fastapi_cdn_host
app = FastAPI()
fastapi_cdn_host.patch_docs(app)
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
- File 是直接继承自 Form 的类。
- 如果把路径操作函数参数的类型声明为 bytes,FastAPI 将以 bytes 形式读取和接收文件内容。
UploadFile 与 bytes 相比有更多优势:
- 使用
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()
:关闭文件。
因为上述方法都是 async
方法,要搭配「await」使用。
在 async 路径操作函数 内,要用以下方式读取文件内容:
contents = await myfile.read()
在普通 def 路径操作函数 内,则可以直接访问 UploadFile.file,例如:
contents = myfile.file.read()
可选上传参数
@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
return {"filename": file.filename}
多文件上传
@app.post("/files/")
async def create_files(files: Annotated[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)
11、请求表单与文件
FastAPI 支持同时使用 File 和 Form 定义文件和表单字段。
from typing import Annotated
from fastapi import FastAPI, File, Form, UploadFile
import fastapi_cdn_host
app = FastAPI()
fastapi_cdn_host.patch_docs(app)
@app.post("/files/")
async def create_file(
file: Annotated[bytes, File()],
fileb: Annotated[UploadFile, File()],
token: Annotated[str, Form()]
):
return {
"file_size": len(file),
"token": token,
"file_content_type": fileb.content_type
}
声明文件可以使用 bytes 或 UploadFile 。