Skip to content

Commit e13bb8b

Browse files
committed
add post
1 parent b333ab1 commit e13bb8b

File tree

24 files changed

+481
-48
lines changed

24 files changed

+481
-48
lines changed
Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
+++
2+
date = '2025-08-09T10:00:00+08:00'
3+
draft = true
4+
title = 'FastAPI Body Advanced Uses'
5+
+++
6+
本篇文章介绍的 FastAPI Request Body 的进阶用法
7+
8+
### Body - Multiple Parameters
9+
首先, 可以将`Path`, `Query` 和 request body 参数声明自由的写在一起
10+
11+
对于 request body 参数可以是可选的, 并且可设置为默认的 `None`
12+
```Python
13+
from typing import Annotated
14+
15+
from fastapi import FastAPI, Path
16+
from pydantic import BaseModel
17+
18+
app = FastAPI()
19+
20+
class Item(BaseModel):
21+
name: str
22+
description: str | None = None
23+
price: float
24+
tax: float | None = None
25+
26+
@app.put("/items/{item_id}")
27+
async def update_item(
28+
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)], # Path
29+
q: str | None = None, # Query
30+
item: Item | None = None, # body
31+
):
32+
results = {"item_id": item_id}
33+
if q:
34+
results.update({"q": q})
35+
if item:
36+
results.update({"item": item})
37+
return results
38+
```
39+
40+
#### Multiple body parameters 多参数请求体
41+
在上面例子中, FastAPI 期望一个包含 `Item` 属性的 JSON body, 例如
42+
```JSON
43+
{
44+
"name": "Foo",
45+
"description": "The pretender",
46+
"price": 42.0,
47+
"tax": 3.2
48+
}
49+
```
50+
但也可以声明多个body parameters, 例如 `item``user`
51+
```Python
52+
from fastapi import FastAPI
53+
from pydantic import BaseModel
54+
55+
app = FastAPI()
56+
57+
58+
class Item(BaseModel):
59+
name: str
60+
description: str | None = None
61+
price: float
62+
tax: float | None = None
63+
64+
65+
class User(BaseModel):
66+
username: str
67+
full_name: str | None = None
68+
69+
@app.put("/items/{item_id}")
70+
async def update_item(item_id: int, item: Item, user: User):
71+
results = {"item_id": item_id, "item": item, "user": user}
72+
return results
73+
```
74+
在这种情况下, FastAPI 会检测到函数有一个 body parameter, 这时会使用中的参数名作为请求体的 key(field names), 并期望如下结构:
75+
```JSON
76+
{
77+
"item": {
78+
"name": "Foo",
79+
"description": "The pretender",
80+
"price": 42.0,
81+
"tax": 3.2
82+
},
83+
"user": {
84+
"username": "dave",
85+
"full_name": "Dave Grohl"
86+
}
87+
}
88+
```
89+
FastAPI 会自动进行请求解析、类型转换、验证, 并在 OpenAPI 文档中反映出这种结构
90+
91+
92+
#### Singular values in body 请求体中的单个参数
93+
`Query``Path` 可以添加额外信息一样, FastAPI 也提供了 `Body` 来对请求参数添加额外信息
94+
95+
例如, 除了 `item``user` 外, 还想在请求体中添加一个 `importance` 字段, 如果直接写 `importance: int` 则会被当作查询参数
96+
97+
可以通过 `Body()` 明确告诉 FastAPI 把它当作一个 body parameter
98+
```Python
99+
@app.put("/items/{item_id}")
100+
async def update_item(
101+
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
102+
):
103+
...
104+
```
105+
这种情况下, FastAPI 会期待如下的请求体:
106+
```JSON
107+
{
108+
"item": {
109+
"name": "Foo",
110+
"description": "The pretender",
111+
"price": 42.0,
112+
"tax": 3.2
113+
},
114+
"user": {
115+
"username": "dave",
116+
"full_name": "Dave Grohl"
117+
},
118+
"importance": 5
119+
}
120+
```
121+
它同样会自动转换数据类型、校验并生成文档
122+
123+
124+
#### Multiple body params and query 多个请求体参数和查询参数
125+
也可以在多请求体参数的基础上, 添加查询参数
126+
```Python
127+
@app.put("/items/{item_id}")
128+
async def update_item(
129+
*, # 强制 key=value
130+
item_id: int,
131+
item: Item,
132+
user: User,
133+
importance: Annotated[int, Body(gt=0)],
134+
q: str | None = None, # 查询参数
135+
):
136+
...
137+
```
138+
139+
140+
#### Embed a single body parameter 嵌入单个请求体参数
141+
假设只有一个请求体参数 `item: Item`, 默认情况下 FastAPI 期望请求体就是一个 `Item` 对应的结构
142+
```JSON
143+
{
144+
"name": "Foo",
145+
"description": "The pretender",
146+
"price": 42.0,
147+
"tax": 3.2
148+
}
149+
```
150+
但若希望如下带有 `item`key 的结构
151+
```JSON
152+
{
153+
"item": {
154+
"name": "Foo",
155+
"description": "The pretender",
156+
"price": 42.0,
157+
"tax": 3.2
158+
}
159+
}
160+
```
161+
那么可以使用 `Body(embed=True)`
162+
163+
```Python
164+
@app.put("/items/{item_id}")
165+
async def update_item(
166+
item_id: int,
167+
item: Annotated[
168+
Item,
169+
Body(embed=True), # embed a single param
170+
]
171+
):
172+
...
173+
```
174+
175+
这将使 FastAPI 将请求体视为嵌套结构, key 为 `item`
176+
177+
178+
179+
180+
### Body - Fields
181+
除了可以在*path* operation (路径操作)函数参数中使用 `Query``Path``Body`来声明额外的验证和数据, 还可以在 Pydantic 模型内部的 `Field` 的字段验证规则和元数据
182+
183+
#### Declare model attributes 声明模型字段属性
184+
首先要导入 Filed
185+
```Python
186+
from pydantic import BaseModel, Field # import Filed
187+
```
188+
可以在模型字段上使用 `Filed` 来添加验证规则和信息
189+
```Python
190+
class Item(BaseModel):
191+
name: str
192+
description: str | None = Field(
193+
default=None, title="项目的描述", max_length=300
194+
)
195+
price: float = Field(gt=0, description="价格必须大于 0")
196+
tax: float | None = None
197+
```
198+
实际上, `Query``Path` 和其他类, 都继承自一个公共的 `Param` 类, 而 `Param``Pydantic``FieldInfo` 类的子类, `pydantic.Field()` 返回的就是一个 `FieldInfo` 实例
199+
200+
201+
### Body - Nested Models
202+
在 FastAPI 中, 可以定义、校验、文档化并使用任意深度嵌套的模型
203+
204+
#### List fields 列表字段
205+
可以将字段定义为某种子类型, 例如 Python 的 `list`
206+
```Python
207+
class Item(BaseModel):
208+
name: str
209+
description: str | None = None
210+
price: float
211+
tax: float | None = None
212+
tags: list = [] # list
213+
```
214+
215+
- List fields with type parameter 带类型参数的列表字段
216+
217+
Python 提供一种"类型参数"的方法, 来指定列表类型
218+
```Python
219+
# Python 3.10+
220+
tags: list[str] = []
221+
```
222+
223+
对于py3.10之前的版本, 需要使用 `typing` 模块
224+
```Python
225+
tags: List[str] = []
226+
```
227+
228+
229+
#### Set types 集和类型
230+
如果不希望 tages 重复, 则使用 `set` 更加合适
231+
```Python
232+
class Item(BaseModel):
233+
...
234+
tags: set[str] = set()
235+
```
236+
这样即使客户端传来重复元素, FastAPI 也会自动去重并返回一个唯一元素集合
237+
238+
#### Nested Models 嵌套模型
239+
Pydantic 的每个字段都可以是另一模型, 从而形成嵌套结构
240+
```Python
241+
from fastapi import FastAPI
242+
from pydantic import BaseModel
243+
244+
app = FastAPI()
245+
246+
class Image(BaseModel):
247+
url: str
248+
name: str
249+
250+
class Item(BaseModel):
251+
...
252+
image: Image | None = None
253+
254+
@app.put("/items/{item_id}")
255+
async def update_item(item_id: int, item: Item):
256+
return {"item_id": item_id, "item": item}
257+
```
258+
此时的 FastAPI 会期望请求体为如下结构:
259+
```JSON
260+
{
261+
"name": "Foo",
262+
"description": "The pretender",
263+
"price": 42.0,
264+
"tax": 3.2,
265+
"tags": ["rock", "metal", "bar"],
266+
"image": {
267+
"url": "http://example.com/baz.jpg",
268+
"name": "The Foo live"
269+
}
270+
}
271+
```
272+
这样使用 FastAPI 会获得:
273+
- 编辑器自动补全
274+
- 类型转换
275+
- 数据校验
276+
- 自动生成文档
277+
278+
279+
#### Special types and validation 特殊类型与验证
280+
除了像 `str`, `int`, `float` 这类 singular types, 还可以使用更加负责的继承于 `str` 的 singular types, 全部类型可以在 [Pydantic's Type Overview](https://docs.pydantic.dev/latest/concepts/types/) 查看
281+
282+
下面是 `HttpUrl` 的例子
283+
```Python
284+
from pydantic import HttpUrl
285+
286+
class Image(BaseModel):
287+
url: HttpUrl
288+
name: str
289+
```
290+
这样会检查 JSON schema 中的 url 是否合法, 并在 OpenAPI 文档中显示
291+
292+
293+
#### Attributes with lists of submodels 带有子模型属性的列表
294+
```Python
295+
class Image(BaseModel):
296+
url: HttpUrl
297+
name: str
298+
299+
300+
class Item(BaseModel):
301+
name: str
302+
description: str | None = None
303+
price: float
304+
tax: float | None = None
305+
tags: set[str] = set()
306+
images: list[Image] | None = None # lists of submodels
307+
```
308+
此时 FastAPI 会期望请求体有一个 `images` 字段, 为 `Image` 对象的列表
309+
```JSON
310+
{
311+
"name": "Foo",
312+
"description": "The pretender",
313+
"price": 42.0,
314+
"tax": 3.2,
315+
"tags": [
316+
"rock",
317+
"metal",
318+
"bar"
319+
],
320+
"images": [
321+
{
322+
"url": "http://example.com/baz.jpg",
323+
"name": "The Foo live"
324+
},
325+
{
326+
"url": "http://example.com/dave.jpg",
327+
"name": "The Baz"
328+
}
329+
]
330+
}
331+
```
332+
333+
334+
#### Deeply nested models 深度嵌套模型
335+
可以定义任意深度的嵌套模型
336+
```Python
337+
from fastapi import FastAPI
338+
from pydantic import BaseModel, HttpUrl
339+
340+
app = FastAPI()
341+
342+
343+
class Image(BaseModel):
344+
url: HttpUrl
345+
name: str
346+
347+
348+
class Item(BaseModel):
349+
name: str
350+
description: str | None = None
351+
price: float
352+
tax: float | None = None
353+
tags: set[str] = set()
354+
images: list[Image] | None = None
355+
356+
357+
class Offer(BaseModel):
358+
name: str
359+
description: str | None = None
360+
price: float
361+
items: list[Item]
362+
363+
@app.post("/offers/")
364+
async def create_offer(offer: Offer):
365+
return offer
366+
```
367+
368+
369+
#### Bodies of pure lists 纯列表请求体
370+
如果请求体的顶层是一个数组(例如上传多个图片), 可以直接将参数类型声明为列表:
371+
```Python
372+
from fastapi import FastAPI
373+
from pydantic import BaseModel, HttpUrl
374+
375+
app = FastAPI()
376+
377+
class Image(BaseModel):
378+
url: HttpUrl
379+
name: str
380+
381+
@app.post("/images/multiple/")
382+
async def create_multiple_images(images: list[Image]):
383+
return images
384+
```
385+
386+
#### Bodies of arbitrary `dict`S 任意字典作为请求体
387+
可以声明请求体为一个字典 (键和值都可指定类型)
388+
```Python
389+
@app.post("/index-weights/")
390+
async def create_index_weights(weights: dict[int, float]):
391+
return weights
392+
```
393+
- 虽然 JSON 标准只支持字符串作为 key, 但 Pydantic 会自动将字符串形式的数字转换为 int
394+
- 因此, 如果客户端发送 `{ "1": 0.1, "2": 0.2 }`, 接收到的将是 `{1: 0.1, 2: 0.2}`

0 commit comments

Comments
 (0)