mirror of
https://github.com/fastapi/fastapi.git
synced 2026-11-07 16:43:13 -05:00
🌐 Update translations for zh (update-outdated) (#16644)
Co-authored-by: pr-submit[bot] <pr-submit[bot]@users.noreply.github.com> Co-authored-by: pr-push[bot] <pr-push[bot]@users.noreply.github.com>
This commit is contained in:
22 files changed
+298
-144
No files matched your search
@@ -13,7 +13,7 @@ from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` 也提供了一个声明“可能为 `None`”的快捷方式:`Optional`。
|
||||
|
||||
@@ -71,7 +71,7 @@ $ uv run pytest
|
||||
这相当于:
|
||||
|
||||
```Python
|
||||
response = client.get('/')
|
||||
response = client.get("/")
|
||||
```
|
||||
|
||||
...我们曾经通过它向 `TestClient` 发出请求。
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
在你的应用前面的**代理**通常会在把请求转发给你的**服务器**之前,临时设置一些请求头,让服务器知道该请求是由代理**转发**的,并告知原始(公网)URL,包括域名、是否使用 HTTPS 等。
|
||||
|
||||
**服务器**程序(例如通过 **FastAPI CLI** 运行的 **Uvicorn**)能够解析这些请求头,然后把这些信息传递给你的应用。
|
||||
由 `fastapi run` 启动的**服务器**能够解析这些请求头,然后把这些信息传递给你的应用。
|
||||
|
||||
但出于安全考虑,由于服务器并不知道自己处在受信任的代理之后,它默认不会解析这些请求头。
|
||||
|
||||
@@ -34,8 +34,6 @@
|
||||
|
||||
```console
|
||||
$ uv run fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -101,7 +99,7 @@ sequenceDiagram
|
||||
|
||||
你可能会有一个代理,为你的应用添加一个路径前缀。
|
||||
|
||||
在这些情况下,你可以使用 `root_path` 来配置你的应用。
|
||||
在这些情况下,你可以使用 `fastapi run --root-path` 来配置服务器。
|
||||
|
||||
`root_path` 是 ASGI 规范(FastAPI 基于该规范,通过 Starlette 构建)提供的机制。
|
||||
|
||||
@@ -117,7 +115,7 @@ sequenceDiagram
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
代理会在将请求传递给应用服务器(可能是通过 FastAPI CLI 运行的 Uvicorn)之前,实时**“移除”**这个**路径前缀**,让你的应用认为它是在 `/app` 被服务,这样你就不需要更新所有代码去包含 `/api/v1` 前缀。
|
||||
代理会在将请求传递给服务器之前,实时**“移除”**这个**路径前缀**,让你的应用认为它是在 `/app` 被服务,这样你就不需要更新所有代码去包含 `/api/v1` 前缀。
|
||||
|
||||
到这里,一切都会像往常一样工作。
|
||||
|
||||
@@ -161,7 +159,7 @@ IP `0.0.0.0` 通常表示程序监听该机器/服务器上的所有可用 IP。
|
||||
}
|
||||
```
|
||||
|
||||
在此示例中,“Proxy” 可以是 **Traefik** 之类的。服务器可以是用 **Uvicorn** 的 **FastAPI CLI** 运行你的 FastAPI 应用。
|
||||
在此示例中,“Proxy” 可以是 **Traefik** 之类的。而 `fastapi run` 会启动运行你的 FastAPI 应用的服务器。
|
||||
|
||||
### 提供 `root_path` { #providing-the-root-path }
|
||||
|
||||
@@ -171,14 +169,10 @@ IP `0.0.0.0` 通常表示程序监听该机器/服务器上的所有可用 IP。
|
||||
|
||||
```console
|
||||
$ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
如果你使用 Hypercorn,它也有 `--root-path` 选项。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
ASGI 规范为这种用例定义了 `root_path`。
|
||||
@@ -195,14 +189,12 @@ ASGI 规范为这种用例定义了 `root_path`。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
然后,如果你这样启动 Uvicorn:
|
||||
然后,如果你这样启动服务器:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -216,30 +208,11 @@ $ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
}
|
||||
```
|
||||
|
||||
### 在 FastAPI 应用中设置 `root_path` { #setting-the-root-path-in-the-fastapi-app }
|
||||
/// warning | 已弃用的 `root_path` 参数
|
||||
|
||||
或者,如果你无法提供类似 `--root-path` 的命令行选项,你可以在创建 FastAPI 应用时设置参数 `root_path`:
|
||||
在 FastAPI 0.145.0 及更早版本中,也可以在 `FastAPI()` 中配置 `root_path`。此参数现已弃用,因为其行为与服务器配置不一致。请改用 `fastapi run --root-path /api/v1`。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
把 `root_path` 传给 `FastAPI` 等同于把命令行选项 `--root-path` 传给 Uvicorn 或 Hypercorn。
|
||||
|
||||
### 关于 `root_path` { #about-root-path }
|
||||
|
||||
请注意,服务器(Uvicorn)不会用这个 `root_path` 做别的事情,只会把它传给应用。
|
||||
|
||||
但是,如果你用浏览器打开 [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app),你会看到正常的响应:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
因此,它不会期望被访问于 `http://127.0.0.1:8000/api/v1/app`。
|
||||
|
||||
Uvicorn 会期望代理以 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶部额外添加 `/api/v1` 前缀是代理的职责。
|
||||
///
|
||||
|
||||
## 关于移除路径前缀的代理 { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
@@ -247,7 +220,7 @@ Uvicorn 会期望代理以 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在
|
||||
|
||||
在很多情况下,默认是代理不会移除路径前缀。
|
||||
|
||||
在这种情况下(没有移除路径前缀),代理会监听类似 `https://myawesomeapp.com`,当浏览器访问 `https://myawesomeapp.com/api/v1/app` 且你的服务器(例如 Uvicorn)监听 `http://127.0.0.1:8000` 时,代理(未移除路径前缀)会以相同路径访问 Uvicorn:`http://127.0.0.1:8000/api/v1/app`。
|
||||
在这种情况下(没有移除路径前缀),代理会监听类似 `https://myawesomeapp.com`,当浏览器访问 `https://myawesomeapp.com/api/v1/app` 且你的服务器监听 `http://127.0.0.1:8000` 时,代理(未移除路径前缀)会以相同路径访问服务器:`http://127.0.0.1:8000/api/v1/app`。
|
||||
|
||||
## 使用 Traefik 进行本地测试 { #testing-locally-with-traefik }
|
||||
|
||||
@@ -302,7 +275,7 @@ Uvicorn 会期望代理以 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在
|
||||
|
||||
这个文件配置 Traefik 使用路径前缀 `/api/v1`。
|
||||
|
||||
随后 Traefik 会把请求转发到运行在 `http://127.0.0.1:8000` 的 Uvicorn。
|
||||
随后 Traefik 会把请求转发到运行在 `http://127.0.0.1:8000` 的服务器。
|
||||
|
||||
现在启动 Traefik:
|
||||
|
||||
@@ -322,15 +295,13 @@ INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
|
||||
|
||||
```console
|
||||
$ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### 查看响应 { #check-the-responses }
|
||||
|
||||
现在,如果你访问 Uvicorn 端口对应的 URL:[http://127.0.0.1:8000/app](http://127.0.0.1:8000/app),你会看到正常响应:
|
||||
现在,如果你访问服务器端口对应的 URL:[http://127.0.0.1:8000/app](http://127.0.0.1:8000/app),你会看到正常响应:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -360,15 +331,15 @@ $ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
当然,这里的想法是每个人都通过代理访问应用,因此带有路径前缀 `/api/v1` 的版本才是“正确”的。
|
||||
|
||||
而不带路径前缀的版本(`http://127.0.0.1:8000/app`)由 Uvicorn 直接提供,仅供_代理_(Traefik)访问。
|
||||
而不带路径前缀的版本(`http://127.0.0.1:8000/app`)由服务器直接提供,仅供_代理_(Traefik)访问。
|
||||
|
||||
这说明了代理(Traefik)如何使用路径前缀,以及服务器(Uvicorn)如何使用 `--root-path` 选项提供的 `root_path`。
|
||||
这说明了代理(Traefik)如何使用路径前缀,以及服务器如何使用 `--root-path` 选项提供的 `root_path`。
|
||||
|
||||
### 查看文档界面 { #check-the-docs-ui }
|
||||
|
||||
有趣的部分来了。✨
|
||||
|
||||
访问应用的“官方”方式应该是通过我们定义的带有路径前缀的代理。因此,正如预期的那样,如果你尝试不带路径前缀、直接由 Uvicorn 提供的文档界面,它将无法工作,因为它期望通过代理访问。
|
||||
访问应用的“官方”方式应该是通过我们定义的带有路径前缀的代理。因此,正如预期的那样,如果你尝试不带路径前缀、直接由服务器提供的文档界面,它将无法工作,因为它期望通过代理访问。
|
||||
|
||||
你可以在 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) 查看:
|
||||
|
||||
@@ -398,7 +369,7 @@ $ uv run fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
如果你传入了自定义的 `servers` 列表,并且存在 `root_path`(因为你的 API 位于代理后面),**FastAPI** 会在列表开头插入一个使用该 `root_path` 的“server”。
|
||||
|
||||
例如:
|
||||
例如,使用相同的 `fastapi run --root-path /api/v1` 服务器配置:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
@@ -455,7 +426,7 @@ OpenAPI 规范中的 `servers` 属性是可选的。
|
||||
|
||||
如果你不希望 **FastAPI** 包含一个使用 `root_path` 的自动服务器,可以使用参数 `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[8] *}
|
||||
|
||||
这样它就不会被包含到 OpenAPI 模式中。
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
在 HTTP 基础授权中,应用需要请求头包含用户名与密码。
|
||||
|
||||
如果没有接收到 HTTP 基础授权,就返回 HTTP 401 `"Unauthorized"` 错误。
|
||||
如果没有接收到 HTTP 基础授权,就返回 HTTP 401 "Unauthorized" 错误。
|
||||
|
||||
并返回响应头 `WWW-Authenticate`,其值为 `Basic`,以及可选的 `realm` 参数。
|
||||
|
||||
@@ -15,7 +15,7 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
|
||||
## 简单的 HTTP 基础授权 { #simple-http-basic-auth }
|
||||
|
||||
* 导入 `HTTPBasic` 与 `HTTPBasicCredentials`
|
||||
* 使用 `HTTPBasic` 创建**安全方案**
|
||||
* 使用 `HTTPBasic` 创建“`security` 方案”
|
||||
* 在*路径操作*的依赖项中使用 `security`
|
||||
* 返回类型为 `HTTPBasicCredentials` 的对象:
|
||||
* 包含发送的 `username` 与 `password`
|
||||
@@ -45,8 +45,10 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
|
||||
这类似于:
|
||||
|
||||
```Python
|
||||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||||
# Return some error
|
||||
if not (credentials.username == "stanleyjobson") or not (
|
||||
credentials.password == "swordfish"
|
||||
):
|
||||
# 返回某个错误
|
||||
...
|
||||
```
|
||||
|
||||
@@ -67,9 +69,9 @@ if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
|
||||
...
|
||||
```
|
||||
|
||||
但就在 Python 比较完 `johndoe` 的第一个字母 `j` 与 `stanleyjobson` 的 `s` 时,Python 就已经知道这两个字符串不相同了,它会这么想,**没必要浪费更多时间执行剩余字母的对比计算了**。应用立刻就会返回**错误的用户或密码**。
|
||||
但就在 Python 比较完 `johndoe` 的第一个字母 `j` 与 `stanleyjobson` 的 `s` 时,它就会返回 `False`,因为 Python 已经知道这两个字符串不相同了,它会这么想,**没必要浪费更多时间执行剩余字母的对比计算了**。应用就会返回**错误的用户名或密码**。
|
||||
|
||||
但接下来,攻击者继续尝试 `stanleyjobsox` 和 密码 `love123`。
|
||||
但接下来,攻击者继续尝试用户名 `stanleyjobsox` 和密码 `love123`。
|
||||
|
||||
应用代码会执行类似下面的操作:
|
||||
|
||||
@@ -78,17 +80,17 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||||
...
|
||||
```
|
||||
|
||||
此时,Python 要对比 `stanleyjobsox` 与 `stanleyjobson` 中的 `stanleyjobso`,才能知道这两个字符串不一样。因此会多花费几微秒来返回**错误的用户或密码**。
|
||||
此时,Python 要对比 `stanleyjobsox` 与 `stanleyjobson` 中的 `stanleyjobso`,才能知道这两个字符串不一样。因此会多花费几微秒来返回**错误的用户名或密码**。
|
||||
|
||||
#### 反应时间对攻击者的帮助 { #the-time-to-answer-helps-the-attackers }
|
||||
|
||||
通过服务器花费了更多微秒才发送**错误的用户或密码**响应,攻击者会知道猜对了一些内容,起码开头字母是正确的。
|
||||
通过服务器花费了更多微秒才发送**错误的用户名或密码**响应,攻击者会知道猜对了一些内容,起码开头的一些字母是正确的。
|
||||
|
||||
然后,他们就可以放弃 `johndoe`,再用类似 `stanleyjobsox` 的内容进行尝试。
|
||||
然后,他们就可以再次尝试,因为他们知道正确的用户名可能更接近 `stanleyjobsox`,而不是 `johndoe`。
|
||||
|
||||
#### **专业**攻击 { #a-professional-attack }
|
||||
|
||||
当然,攻击者不用手动操作,而是编写每秒能执行成千上万次测试的攻击程序,每次都会找到更多正确字符。
|
||||
当然,攻击者不用手动操作,而是编写每秒可能执行数千甚至数百万次测试的攻击程序,每次只会多找到一个正确字符。
|
||||
|
||||
但是,在您的应用的**帮助**下,攻击者利用时间差,就能在几分钟或几小时内,以这种方式猜出正确的用户名和密码。
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
render_macros: false
|
||||
---
|
||||
|
||||
# 模板 { #templates }
|
||||
|
||||
你可以在 **FastAPI** 中使用任何你想用的模板引擎。
|
||||
@@ -63,14 +67,10 @@ $ uv add jinja2
|
||||
|
||||
在包含如下语句的 HTML 中:
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jinja
|
||||
Item ID: {{ id }}
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
...它会显示你传入的 "context" `dict` 中取得的 `id`:
|
||||
|
||||
```Python
|
||||
@@ -89,22 +89,48 @@ Item ID: 42
|
||||
|
||||
所以,该部分:
|
||||
|
||||
{% raw %}
|
||||
|
||||
```jinja
|
||||
<a href="{{ url_for('read_item', id=id) }}">
|
||||
```
|
||||
|
||||
{% endraw %}
|
||||
|
||||
...将生成一个链接,指向由*路径操作函数* `read_item(id=id)` 处理的同一个 URL。
|
||||
|
||||
例如,当 ID 为 `42` 时,会渲染成:
|
||||
`url_for()` 根据当前请求返回一个包含协议和主机的绝对 URL。
|
||||
|
||||
例如,如果你的应用部署在 `https://example.com`,当 ID 为 `42` 时,会渲染成:
|
||||
|
||||
```html
|
||||
<a href="https://example.com/items/42">
|
||||
```
|
||||
|
||||
### 模板中的 URL 路径 { #url-paths-in-templates }
|
||||
|
||||
对于指向同一站点的链接,你可以使用 `.path` 属性仅获取 URL 路径,例如,在 `templates/item_path.html` 中:
|
||||
|
||||
```jinja hl_lines="4 7"
|
||||
{!../../docs_src/templates/templates/item_path.html!}
|
||||
```
|
||||
|
||||
将模板名称传递给 `TemplateResponse`:
|
||||
|
||||
{* ../../docs_src/templates/tutorial002_py310.py ln[14:18] hl[17] *}
|
||||
|
||||
使用同一个示例,条目链接会渲染成:
|
||||
|
||||
```html
|
||||
<a href="/items/42">
|
||||
```
|
||||
|
||||
开头的 `/` 表示该路径相对于当前站点的根路径。
|
||||
|
||||
`.path` 属性不包含协议、主机、查询参数和片段。对于指向其他主机的链接,请使用完整 URL。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
如果你的应用位于代理之后,请按照[代理背后](behind-a-proxy.md)中的说明配置转发请求头和 `root_path`。
|
||||
|
||||
///
|
||||
|
||||
## 模板与静态文件 { #templates-and-static-files }
|
||||
|
||||
你还可以在模板内部使用 `url_for()`,例如将它与你挂载的 `name="static"` 的 `StaticFiles` 一起使用。
|
||||
|
||||
@@ -15,7 +15,7 @@ results = await some_library()
|
||||
然后,通过 `async def` 声明你的 *路径操作函数*:
|
||||
|
||||
```Python hl_lines="2"
|
||||
@app.get('/')
|
||||
@app.get("/")
|
||||
async def read_results():
|
||||
results = await some_library()
|
||||
return results
|
||||
@@ -32,7 +32,7 @@ async def read_results():
|
||||
如果你正在使用一个第三方库和某些组件(比如:数据库、API、文件系统等)进行通信,而该第三方库不支持使用 `await`(目前大多数数据库库都是这样),这种情况你可以像平常那样使用 `def` 声明一个路径操作函数,就像这样:
|
||||
|
||||
```Python hl_lines="2"
|
||||
@app.get('/')
|
||||
@app.get("/")
|
||||
def results():
|
||||
results = some_library()
|
||||
return results
|
||||
@@ -221,7 +221,7 @@ Python 的现代版本支持通过一种叫**“协程”**——使用 `async`
|
||||
|
||||
---
|
||||
|
||||
这将是与汉堡的类似故事。🍔
|
||||
这就是汉堡故事对应的并行版本。🍔
|
||||
|
||||
一种更“贴近生活”的例子,想象一家银行。
|
||||
|
||||
@@ -235,7 +235,7 @@ Python 的现代版本支持通过一种叫**“协程”**——使用 `async`
|
||||
|
||||
### 汉堡结论 { #burger-conclusion }
|
||||
|
||||
在“你与恋人一起吃汉堡”的这个场景中,因为有很多人在等待 🕙,使用并发系统更有意义 ⏸🔀⏯。
|
||||
在“你与恋人一起吃汉堡”的这个场景中,因为有很多等待 🕙,使用并发系统更有意义 ⏸🔀⏯。
|
||||
|
||||
大多数 Web 应用都是这样的。
|
||||
|
||||
@@ -288,7 +288,7 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
|
||||
* **音频**或**图像**处理;
|
||||
* **计算机视觉**:一幅图像由数百万像素组成,每个像素有 3 种颜色值,处理通常需要同时对这些像素进行计算;
|
||||
* **机器学习**:它通常需要大量的“矩阵”和“向量”乘法。想象一个包含数字的巨大电子表格,并同时将所有数字相乘;
|
||||
* **深度学习**:这是机器学习的一个子领域,同样适用。只是没有一个数字的电子表格可以相乘,而是一个庞大的数字集合,在很多情况下,你需要使用一个特殊的处理器来构建和/或使用这些模型。
|
||||
* **深度学习**:这是机器学习的一个子领域,同样适用。只是需要做乘法的不再是单个数字电子表格,而是一大批这样的表格,在很多情况下,你需要使用一个特殊的处理器来构建和/或使用这些模型。
|
||||
|
||||
### 并发 + 并行: Web + 机器学习 { #concurrency-parallelism-web-machine-learning }
|
||||
|
||||
@@ -343,7 +343,7 @@ burgers = get_burgers(2)
|
||||
因此,如果你使用的库告诉你可以使用 `await` 调用它,则需要使用 `async def` 创建路径操作函数,如:
|
||||
|
||||
```Python hl_lines="2-3"
|
||||
@app.get('/burgers')
|
||||
@app.get("/burgers")
|
||||
async def read_burgers():
|
||||
burgers = await get_burgers(2)
|
||||
return burgers
|
||||
@@ -419,7 +419,7 @@ Starlette(和 **FastAPI**)是基于 [AnyIO](https://anyio.readthedocs.io/en/
|
||||
|
||||
如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的 `def` 定义普通的仅计算路径操作函数,以获得微小的性能增益(大约 100 纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用 `async def`,除非路径操作函数内使用执行阻塞 <abbr title="Input/Output - 输入/输出:磁盘读写,网络通信。">I/O</abbr> 的代码。
|
||||
|
||||
在这两种情况下,**FastAPI** [仍然会更快](index.md#performance)(或至少与之前的框架相当)。
|
||||
在这两种情况下,**FastAPI** 很可能[仍然会更快](index.md#performance)(或至少与之前的框架相当)。
|
||||
|
||||
### 依赖 { #dependencies }
|
||||
|
||||
|
||||
+16
-16
@@ -17,7 +17,7 @@
|
||||
|
||||
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui),可交互式操作,能在浏览器中直接调用和测试你的 API。
|
||||
|
||||

|
||||

|
||||
|
||||
* 另外的 API 文档:[**ReDoc**](https://github.com/Redocly/redoc)。
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
全部都基于标准的 **Python 类型** 声明(感谢 Pydantic)。没有新的语法需要学习。只需要标准的现代 Python。
|
||||
|
||||
如果你需要2分钟来学习如何使用 Python 类型(即使你不使用 FastAPI),看看这个简短的教程:[Python 类型](python-types.md)。
|
||||
如果你需要2分钟来复习如何使用 Python 类型(即使你不使用 FastAPI),看看这个简短的教程:[Python 类型](python-types.md)。
|
||||
|
||||
编写带有类型标注的标准 Python:
|
||||
|
||||
@@ -36,6 +36,7 @@ from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# 将变量声明为 str
|
||||
# 并在函数内获得编辑器支持
|
||||
def main(user_id: str):
|
||||
@@ -63,7 +64,6 @@ second_user_data = {
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
|
||||
/// note | 注意
|
||||
|
||||
`**second_user_data` 意思是:
|
||||
@@ -86,11 +86,11 @@ my_second_user: User = User(**second_user_data)
|
||||
|
||||
* 在 [Visual Studio Code](https://code.visualstudio.com/) 中:
|
||||
|
||||

|
||||

|
||||
|
||||
* 在 [PyCharm](https://www.jetbrains.com/pycharm/) 中:
|
||||
|
||||

|
||||

|
||||
|
||||
你将能进行代码补全,这是在之前你可能曾认为不可能的事。例如,在来自请求 JSON 体(可能是嵌套的)中的键 `price`。
|
||||
|
||||
@@ -98,7 +98,7 @@ my_second_user: User = User(**second_user_data)
|
||||
|
||||
### 简洁 { #short }
|
||||
|
||||
任何类型都有合理的**默认值**,任何地方都有可选配置。所有的参数被微调,来满足你的需求,定义成你需要的 API。
|
||||
任何类型都有合理的**默认值**,任何地方都有可选配置。所有的参数都可以微调,来满足你的需求,定义成你需要的 API。
|
||||
|
||||
但是默认情况下,一切都能**“顺利工作”**。
|
||||
|
||||
@@ -107,7 +107,7 @@ my_second_user: User = User(**second_user_data)
|
||||
* 校验大部分(甚至所有?)的 Python **数据类型**,包括:
|
||||
* JSON 对象 (`dict`)。
|
||||
* JSON 数组 (`list`) 定义成员类型。
|
||||
* 字符串 (`str`) 字段,定义最小或最大长度。
|
||||
* 字符串 (`str`) 字段,定义最小和最大长度。
|
||||
* 数字 (`int`, `float`) 有最大值和最小值,等等。
|
||||
|
||||
* 校验更特殊的类型,比如:
|
||||
@@ -133,18 +133,18 @@ OpenAPI 中定义的所有安全模式,包括:
|
||||
|
||||
加上来自 Starlette(包括 **session cookies**)的所有安全特性。
|
||||
|
||||
所有的这些都是可复用的工具和组件,可以轻松与你的系统,数据仓库,关系型以及 NoSQL 数据库等等集成。
|
||||
所有的这些都是可复用的工具和组件,可以轻松与你的系统,数据存储,关系型以及 NoSQL 数据库等等集成。
|
||||
|
||||
### 依赖注入 { #dependency-injection }
|
||||
|
||||
FastAPI 有一个使用非常简单,但是非常强大的<dfn title='也称为 "components"、"resources"、"services"、"providers"'><strong>依赖注入</strong></dfn>系统。
|
||||
FastAPI 有一个使用非常简单,但是非常强大的<dfn title='也称为 "组件"、"资源"、"服务"、"提供者"'><strong>依赖注入</strong></dfn>系统。
|
||||
|
||||
* 甚至依赖也可以有依赖,创建一个层级或者**“图”依赖**。
|
||||
* 所有**自动化处理**都由框架完成。
|
||||
* 所有的依赖关系都可以从请求中获取数据,并且**增加了路径操作**约束和自动文档生成。
|
||||
* 即使是在依赖项中定义的*路径操作*参数也会**自动验证**。
|
||||
* 支持复杂的用户身份认证系统,**数据库连接**等等。
|
||||
* **不依赖**数据库,前端等。但是和它们集成很简单。
|
||||
* **无需迁就**数据库,前端等。但是和它们集成很简单。
|
||||
|
||||
### 无限制的“插件” { #unlimited-plug-ins }
|
||||
|
||||
@@ -169,7 +169,7 @@ FastAPI 有一个使用非常简单,但是非常强大的<dfn title='也称为
|
||||
* 令人惊叹的性能。它是[Python 可用的最快的框架之一,和 **NodeJS** 及 **Go** 相当](https://github.com/encode/starlette#performance)。
|
||||
* **支持 WebSocket**。
|
||||
* 进程内后台任务。
|
||||
* Startup 和 shutdown 事件。
|
||||
* 启动和关闭事件。
|
||||
* 测试客户端基于 HTTPX。
|
||||
* **CORS**、GZip、静态文件、流响应。
|
||||
* 支持 **Session 和 Cookie**。
|
||||
@@ -180,23 +180,23 @@ FastAPI 有一个使用非常简单,但是非常强大的<dfn title='也称为
|
||||
|
||||
**FastAPI** 与 [**Pydantic**](https://pydantic.dev/docs/) 完全兼容(并基于它构建)。所以,你有的其他的 Pydantic 代码也能正常工作。
|
||||
|
||||
兼容包括基于 Pydantic 的外部库,例如用于数据库的 <abbr title="Object-Relational Mapper - 对象关系映射">ORM</abbr>s、<abbr title="Object-Document Mapper - 对象文档映射">ODM</abbr>s。
|
||||
兼容包括基于 Pydantic 的外部库,例如用于数据库的 <abbr title="Object-Relational Mapper - 对象关系映射器">ORM</abbr> 和 <abbr title="Object-Document Mapper - 对象文档映射器">ODM</abbr>。
|
||||
|
||||
这也意味着在很多情况下,你可以将从请求中获得的相同对象**直接传到数据库**,因为所有的验证都是自动的。
|
||||
|
||||
反之亦然,在很多情况下,你也可以将从数据库中获取的对象**直接传到客户端**。
|
||||
|
||||
通过 **FastAPI** 你可以获得所有 **Pydantic**(FastAPI 基于 Pydantic 做了所有的数据处理):
|
||||
通过 **FastAPI** 你可以获得所有 **Pydantic** 的特性(FastAPI 基于 Pydantic 做了所有的数据处理):
|
||||
|
||||
* **不烧脑**:
|
||||
* 没有新的模式定义 micro-language 需要学习。
|
||||
* 没有新的模式定义微型语言需要学习。
|
||||
* 如果你知道 Python 类型,你就知道如何使用 Pydantic。
|
||||
* 和你 **<abbr title="Integrated Development Environment - 集成开发环境: 类似于代码编辑器">IDE</abbr>/<dfn title="一个检查代码错误的程序">linter</dfn>/brain** 适配:
|
||||
* 和你 **<abbr title="Integrated Development Environment - 集成开发环境: 类似于代码编辑器">IDE</abbr>/<dfn title="一个检查代码错误的程序">linter</dfn>/大脑** 适配:
|
||||
* 因为 pydantic 数据结构仅仅是你定义的类的实例;自动补全,linting,mypy 以及你的直觉应该可以和你验证的数据一起正常工作。
|
||||
* 验证**复杂结构**:
|
||||
* 使用分层的 Pydantic 模型,Python `typing` 的 `List` 和 `Dict` 等等。
|
||||
* 验证器使我们能够简单清楚地将复杂的数据模式定义、检查并记录为 JSON Schema。
|
||||
* 你可以拥有深度**嵌套的 JSON** 对象并对它们进行验证和注释。
|
||||
* **可扩展**:
|
||||
* Pydantic 允许定义自定义数据类型或者你可以用验证器装饰器对被装饰的模型上的方法扩展验证。
|
||||
* Pydantic 允许定义自定义数据类型,或者你可以通过模型上使用验证器装饰器装饰的方法扩展验证。
|
||||
* 100% 测试覆盖率。
|
||||
@@ -105,7 +105,7 @@ FastAPI 是一个用于构建 API 的现代、快速(高性能)的 Web 框
|
||||
</div>
|
||||
|
||||
<div class="fastapi-opinions__panel" id="fo-panel-microsoft" role="tabpanel" aria-labelledby="fo-tab-microsoft" tabindex="0">
|
||||
<blockquote class="fastapi-opinions__quote">“我最近大量使用 <strong>FastAPI</strong>。我实际上计划把它用于我团队在 <strong>微软的机器学习(ML)服务</strong>。其中一些正在集成进核心 <strong>Windows</strong> 产品以及一些 <strong>Office</strong> 产品。”</blockquote>
|
||||
<blockquote class="fastapi-opinions__quote">“我最近大量使用 <strong>FastAPI</strong>。我实际上计划把它用于我团队在 <strong>微软的所有机器学习(ML)服务</strong>。其中一些正在集成进核心 <strong>Windows</strong> 产品以及一些 <strong>Office</strong> 产品。”</blockquote>
|
||||
<div class="fastapi-opinions__attr">— Kabir Khan,<strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26">(参考)</a></div>
|
||||
</div>
|
||||
<div class="fastapi-opinions__panel" id="fo-panel-uber" role="tabpanel" aria-labelledby="fo-tab-uber" tabindex="0" hidden>
|
||||
@@ -125,7 +125,7 @@ FastAPI 是一个用于构建 API 的现代、快速(高性能)的 Web 框
|
||||
|
||||
<div class="only-github" markdown="1">
|
||||
|
||||
「_[...] 我最近大量使用 **FastAPI**。[...] 我实际上计划把它用于我团队在 **微软的机器学习(ML)服务**。其中一些正在集成进核心 **Windows** 产品以及一些 **Office** 产品。_」
|
||||
「_[...] 我最近大量使用 **FastAPI**。[...] 我实际上计划把它用于我团队在 **微软的所有机器学习(ML)服务**。其中一些正在集成进核心 **Windows** 产品以及一些 **Office** 产品。_」
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26"><small>(参考)</small></a></div>
|
||||
|
||||
@@ -235,7 +235,7 @@ async def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
**注意**:
|
||||
|
||||
如果你不确定,请查看文档中 _"In a hurry?"_ 章节的 [`async` 和 `await`](https://fastapi.tiangolo.com/zh/async/#in-a-hurry) 部分。
|
||||
如果你不确定,请查看文档中关于 [`async` 和 `await`](https://fastapi.tiangolo.com/zh/async/#in-a-hurry) 的 _"急用?"_ 章节。
|
||||
|
||||
</details>
|
||||
|
||||
@@ -409,9 +409,9 @@ item: Item
|
||||
* 路径参数。
|
||||
* 查询参数。
|
||||
* Cookies。
|
||||
* Headers。
|
||||
* Forms。
|
||||
* Files。
|
||||
* 请求头。
|
||||
* 表单。
|
||||
* 文件。
|
||||
* <dfn title="也被称为:序列化、解析、编组">转换</dfn>输出数据:从 Python 数据和类型转换为网络数据(JSON):
|
||||
* 转换 Python 类型(`str`、`int`、`float`、`bool`、`list` 等)。
|
||||
* `datetime` 对象。
|
||||
@@ -473,10 +473,10 @@ item: Item
|
||||
|
||||
**剧透警告**:教程 - 用户指南包括:
|
||||
|
||||
* 来自不同位置的**参数**声明:**headers**、**cookies**、**form 字段**和**文件**。
|
||||
* 来自不同位置的**参数**声明:**请求头**、**cookies**、**表单字段**和**文件**。
|
||||
* 如何设置**校验约束**,如 `maximum_length` 或 `regex`。
|
||||
* 功能强大且易用的 **<dfn title="也被称为:组件、资源、提供者、服务、可注入项">依赖注入</dfn>** 系统。
|
||||
* 安全与认证,包括对 **OAuth2**、**JWT tokens** 和 **HTTP Basic** 认证的支持。
|
||||
* 安全与认证,包括对结合 **JWT tokens** 的 **OAuth2** 和 **HTTP Basic** 认证的支持。
|
||||
* 更高级(但同样简单)的 **多层嵌套 JSON 模型** 声明技巧(得益于 Pydantic)。
|
||||
* 通过 [Strawberry](https://strawberry.rocks) 等库进行 **GraphQL** 集成。
|
||||
* 许多额外特性(归功于 Starlette),例如:
|
||||
@@ -544,7 +544,7 @@ Pydantic 使用:
|
||||
|
||||
Starlette 使用:
|
||||
|
||||
* [`httpx`](https://www.python-httpx.org) - 使用 `TestClient` 时需要。
|
||||
* [`httpx2`](https://pydantic.dev/docs/httpx2/)(或 `httpx`)- 供 `TestClient` 使用。
|
||||
* [`jinja2`](https://jinja.palletsprojects.com) - 使用默认模板配置时需要。
|
||||
* [`python-multipart`](https://github.com/Kludex/python-multipart) - 使用 `request.form()` 支持表单<dfn title="将 HTTP 请求中的字符串转换为 Python 数据">「解析」</dfn>时需要。
|
||||
|
||||
|
||||
@@ -155,15 +155,14 @@ from app.routers import items
|
||||
|
||||
```Python hl_lines="1"
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
...
|
||||
async def read_item(item_id: str): ...
|
||||
```
|
||||
|
||||
...前缀不能以 `/` 作为结尾。
|
||||
|
||||
因此,本例中的前缀为 `/items`。
|
||||
|
||||
我们还可以添加一个 `tags` 列表和额外的 `responses` 列表,这些参数将应用于此路由器中包含的所有*路径操作*。
|
||||
我们还可以添加一个 `tags` 列表和额外的 `responses`,这些参数将应用于此路由器中包含的所有*路径操作*。
|
||||
|
||||
我们可以添加一个 `dependencies` 列表,这些依赖项将被添加到路由器中的所有*路径操作*中,并将针对向它们发起的每个请求执行/解决。
|
||||
|
||||
@@ -269,13 +268,13 @@ from ...dependencies import get_token_header
|
||||
* 在该父包中,找到 `dependencies` 模块(位于 `app/dependencies.py` 的文件)...
|
||||
* 然后从中导入函数 `get_token_header`。
|
||||
|
||||
这将引用 `app/` 的往上一级,带有其自己的 `__init __.py` 等文件的某个包。但是我们并没有这个包。因此,这将在我们的示例中引发错误。🚨
|
||||
这将引用 `app/` 的往上一级,带有其自己的 `__init__.py` 等文件的某个包。但是我们并没有这个包。因此,这将在我们的示例中引发错误。🚨
|
||||
|
||||
但是现在你知道了它的工作原理,因此无论它们多么复杂,你都可以在自己的应用程序中使用相对导入。🤓
|
||||
|
||||
### 添加一些自定义的 `tags`、`responses` 和 `dependencies` { #add-some-custom-tags-responses-and-dependencies }
|
||||
|
||||
我们不打算在每个*路径操作*中添加前缀 `/items` 或 `tags =["items"]`,因为我们将它们添加到了 `APIRouter` 中。
|
||||
我们不打算在每个*路径操作*中添加前缀 `/items` 或 `tags=["items"]`,因为我们将它们添加到了 `APIRouter` 中。
|
||||
|
||||
但是我们仍然可以添加*更多*将会应用于特定的*路径操作*的 `tags`,以及一些特定于该*路径操作*的额外 `responses`:
|
||||
|
||||
@@ -313,7 +312,7 @@ from ...dependencies import get_token_header
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
由于文件 `app/routers/users.py` 和 `app/routers/items.py` 是同一 Python 包 `app` 一个部分的子模块,因此我们可以使用单个点 ` .` 通过「相对导入」来导入它们。
|
||||
由于文件 `app/routers/users.py` 和 `app/routers/items.py` 是同一 Python 包 `app` 一个部分的子模块,因此我们可以使用单个点 `.` 通过「相对导入」来导入它们。
|
||||
|
||||
### 导入是如何工作的 { #how-the-importing-works }
|
||||
|
||||
@@ -422,7 +421,7 @@ from .routers.users import router
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
但是我们仍然希望在包含 `APIRouter` 时设置一个自定义的 `prefix`,以便其所有*路径操作*以 `/admin` 开头,我们希望使用本项目已经有的 `dependencies` 保护它,并且我们希望它包含自定义的 `tags` 和 `responses`。
|
||||
但是我们仍然希望在包含 `APIRouter` 时设置一个自定义的 `prefix`,以便其所有*路径操作*以 `/admin` 开头,我们希望使用本项目已经有的 `dependencies` 保护它,并且我们希望它包含 `tags` 和 `responses`。
|
||||
|
||||
我们可以通过将这些参数传递给 `app.include_router()` 来完成所有的声明,而不必修改原始的 `APIRouter`:
|
||||
|
||||
|
||||
@@ -42,13 +42,13 @@ Pydantic 的 `Field` 返回也是 `FieldInfo` 的类实例。
|
||||
|
||||
## 添加更多信息 { #add-extra-information }
|
||||
|
||||
`Field`、`Query`、`Body` 等对象里可以声明更多信息,并且 JSON Schema 中也会集成这些信息。
|
||||
可以在 `Field`、`Query`、`Body` 等对象里使用 `json_schema_extra` 声明更多信息,并且生成的 JSON Schema 中也会集成这些信息。
|
||||
|
||||
*声明示例*一章中将详细介绍添加更多信息的知识。
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
传递给 `Field` 的额外键也会出现在你的应用生成的 OpenAPI 架构中。
|
||||
在 `json_schema_extra` 中传递的额外键也会出现在你的应用生成的 OpenAPI 架构中。
|
||||
由于这些键不一定属于 OpenAPI 规范的一部分,某些 OpenAPI 工具(例如 [OpenAPI 验证器](https://validator.swagger.io/))可能无法处理你生成的架构。
|
||||
|
||||
///
|
||||
@@ -57,4 +57,4 @@ Pydantic 的 `Field` 返回也是 `FieldInfo` 的类实例。
|
||||
|
||||
Pydantic 的 `Field` 可以为模型属性声明更多校验和元数据。
|
||||
|
||||
传递 JSON Schema 元数据还可以使用更多关键字参数。
|
||||
还可以使用 `json_schema_extra` 传递额外的 JSON Schema 元数据。
|
||||
@@ -71,6 +71,22 @@
|
||||
}
|
||||
```
|
||||
|
||||
## 组合参数模型 { #combine-parameter-models }
|
||||
|
||||
你可以将多个 cookie 参数模型与单独的 cookie 参数及依赖项组合使用。每个模型都会独立提取和验证,具体说明请参阅[查询参数模型](query-param-models.md#combine-parameter-models)。
|
||||
|
||||
每个模型的 `extra` 配置适用于所有 cookie 参数,包括在其他模型或依赖项中声明的参数。使用 `extra="forbid"` 时,该模型之外的任何 cookie 都会被拒绝。使用 `extra="allow"` 时,这些 cookie 会作为模型的额外字段包含在内。
|
||||
|
||||
## 可选 Cookie 参数模型 { #optional-cookie-parameter-models }
|
||||
|
||||
使用 `| None` 并将默认值设为 `None` 来声明 cookie 参数模型,即可将这组参数设为可选。
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial003_an_py310.py hl[15] *}
|
||||
|
||||
如果既未发送 `theme` 也未发送 `locale`,则 `preferences` 为 `None`。如果发送了其中任意一个,FastAPI 就会验证模型,并要求两个字段都存在。
|
||||
|
||||
默认值、额外字段和 OpenAPI 的规则与[可选查询参数模型](query-param-models.md#optional-query-parameter-models)相同。
|
||||
|
||||
## 总结 { #summary }
|
||||
|
||||
你可以使用 **Pydantic 模型**在 **FastAPI** 中声明 <dfn title="临走前再吃一块小饼干吧。🍪">**cookie**</dfn>。😎
|
||||
@@ -58,14 +58,16 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
||||
|
||||
如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。
|
||||
|
||||
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行<dfn title="用于存储已计算/生成的值,以便复用而无需再次计算的实用工具/系统">「缓存」</dfn>,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
|
||||
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行<dfn title="用于存储已计算/生成的值,以便复用而无需再次计算的实用工具/系统">「缓存」</dfn>,并把它传递给同一请求中所有需要使用该返回值的「依赖方」。
|
||||
|
||||
在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False`:
|
||||
在高级使用场景中,如果不想使用「缓存」值,而是需要在同一请求的每一步操作(可能多次)中都实际调用依赖项,可以在使用 `Depends` 时设置参数 `use_cache=False`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
```Python hl_lines="2"
|
||||
async def needy_dependency(
|
||||
fresh_value: Annotated[str, Depends(get_value, use_cache=False)],
|
||||
):
|
||||
return {"fresh_value": fresh_value}
|
||||
```
|
||||
|
||||
|
||||
@@ -55,10 +55,10 @@ print(user_dict)
|
||||
|
||||
```Python
|
||||
{
|
||||
'username': 'john',
|
||||
'password': 'secret',
|
||||
'email': 'john.doe@example.com',
|
||||
'full_name': None,
|
||||
"username": "john",
|
||||
"password": "secret",
|
||||
"email": "john.doe@example.com",
|
||||
"full_name": None,
|
||||
}
|
||||
```
|
||||
|
||||
@@ -87,10 +87,10 @@ UserInDB(
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
password = user_dict["password"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
username=user_dict["username"],
|
||||
password=user_dict["password"],
|
||||
email=user_dict["email"],
|
||||
full_name=user_dict["full_name"],
|
||||
)
|
||||
```
|
||||
|
||||
@@ -125,11 +125,11 @@ UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
password = user_dict["password"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
hashed_password = hashed_password,
|
||||
username=user_dict["username"],
|
||||
password=user_dict["password"],
|
||||
email=user_dict["email"],
|
||||
full_name=user_dict["full_name"],
|
||||
hashed_password=hashed_password,
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -67,6 +67,26 @@
|
||||
|
||||
///
|
||||
|
||||
## 组合参数模型 { #combine-parameter-models }
|
||||
|
||||
你可以将多个 header 参数模型与单独的 header 参数及依赖项组合使用。每个模型都会独立提取和验证,如[查询参数模型](query-param-models.md#combine-parameter-models)中所述。
|
||||
|
||||
每个模型的 `extra` 配置都适用于所有 header 参数,包括在其他模型或依赖项中声明的参数。使用 `extra="forbid"` 时,不属于该模型的任何 header 都会被拒绝。使用 `extra="allow"` 时,这些 headers 会作为模型的额外字段包含在内。
|
||||
|
||||
每个模型在读取请求和生成 OpenAPI 时,都会使用各自的 `Header(convert_underscores=...)` 设置。显式指定的字段别名会原样用作 header 名称。
|
||||
|
||||
## 可选的 Header 参数模型 { #optional-header-parameter-models }
|
||||
|
||||
使用 `| None` 声明 header 参数模型,并将默认值设为 `None`,即可将这组参数设为可选。
|
||||
|
||||
{* ../../docs_src/header_param_models/tutorial004_an_py310.py hl[15] *}
|
||||
|
||||
如果既未发送 `x-client-name` 也未发送 `x-client-version`,则 `client` 为 `None`。如果发送了其中任意一个,FastAPI 就会验证模型,并要求提供这两个字段。
|
||||
|
||||
默认值、额外字段和 OpenAPI 的规则与[可选的查询参数模型](query-param-models.md#optional-query-parameter-models)相同。
|
||||
|
||||
`Host` 等 headers 也遵循模型的 `extra` 配置。使用 `extra="allow"` 时,即使未提供任何一个已声明的字段,这些 headers 也会使模型被视为已提供。
|
||||
|
||||
## 总结 { #summary }
|
||||
|
||||
您可以使用 **Pydantic 模型**在 **FastAPI** 中声明 **headers**。😎
|
||||
@@ -28,7 +28,7 @@ FastAPI 在 0.95.0 版本添加了对 `Annotated` 的支持(并开始推荐使
|
||||
|
||||
/// note | 注意
|
||||
|
||||
路径参数总是必需的,因为它必须是路径的一部分。即使你将其声明为 `None` 或设置了默认值,也不会产生任何影响,它依然始终是必需参数。
|
||||
路径参数总是必需的,因为它必须是 URL 路径的一部分。在其类型中添加 `None` 并不会使其变为可选参数。使用 `Path` 声明的参数不能有默认值。
|
||||
|
||||
///
|
||||
|
||||
@@ -68,7 +68,7 @@ FastAPI 在 0.95.0 版本添加了对 `Annotated` 的支持(并开始推荐使
|
||||
|
||||
///
|
||||
|
||||
这里有一个小技巧,可能会很方便,但你并不会经常需要它。
|
||||
这里有一个**小技巧**,可能会很方便,但你并不会经常需要它。
|
||||
|
||||
如果你想要:
|
||||
|
||||
@@ -81,7 +81,7 @@ FastAPI 在 0.95.0 版本添加了对 `Annotated` 的支持(并开始推荐使
|
||||
|
||||
在函数的第一个参数位置传入 `*`。
|
||||
|
||||
Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数都应该作为关键字参数(键值对)来调用,也被称为 <abbr title="来自:K-ey W-ord Arg-uments"><code>kwargs</code></abbr>。即使它们没有默认值。
|
||||
Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数都应该作为关键字参数(键值对)来调用,也被称为 <abbr title="来自:K-ey W-ord Arg-uments - 关键字参数"><code>kwargs</code></abbr>。即使它们没有默认值。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
@@ -95,7 +95,7 @@ Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数
|
||||
|
||||
使用 `Query` 和 `Path`(以及你稍后会看到的其他类)你可以声明数值约束。
|
||||
|
||||
在这里,使用 `ge=1` 后,`item_id` 必须是一个整数,值要「`g`reater than or `e`qual」1。
|
||||
在这里,使用 `ge=1` 后,`item_id` 必须是一个整数,值要大于等于(`g`reater than or `e`qual)`1`。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ FastAPI 从 `0.115.0` 版本开始支持这个特性。🤓
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
|
||||
|
||||
**FastAPI** 将会从请求的**查询参数**中**提取**出**每个字段**的数据,并将其提供给你定义的 Pydantic 模型。
|
||||
**FastAPI** 将会从请求的**查询参数**中**提取**出**每个字段**的数据,并将你定义的 Pydantic 模型提供给你。
|
||||
|
||||
## 查看文档 { #check-the-docs }
|
||||
|
||||
@@ -57,6 +57,42 @@ https://example.com/items/?limit=10&tool=plumbus
|
||||
}
|
||||
```
|
||||
|
||||
## 组合参数模型 { #combine-parameter-models }
|
||||
|
||||
你可以声明多个查询参数模型,并将它们与单独的查询参数或依赖项组合使用。
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial003_an_py310.py hl[9:15,20:22] *}
|
||||
|
||||
请求参数仍处于同一层级。
|
||||
|
||||
```http
|
||||
https://example.com/items/?limit=5&offset=10&tags=red&tags=blue&search=shirt
|
||||
```
|
||||
|
||||
查询字符串中没有 `pagination` 或 `filters` 键。每个模型都使用自己的默认值、字段验证器和模型验证器独立验证查询参数。在依赖项中声明模型或单独的参数时也是如此。
|
||||
|
||||
如果模型共享参数名或别名,每个模型都会独立验证该请求参数。对于相互独立的值,请使用不同的名称或别名。OpenAPI 只会列出共享参数一次,就像处理与依赖项共享的参数一样,因此共享参数的声明应描述相同的类型和约束。
|
||||
|
||||
每个模型的 `extra` 配置都适用于所有查询参数,包括在其他模型或依赖项中声明的参数。使用 `extra="forbid"` 时,该模型之外的任何参数都会被拒绝。使用 `extra="allow"` 时,这些参数会作为模型的额外数据被包含在内。
|
||||
|
||||
这同样适用于 `Header` 和 `Cookie` 模型。
|
||||
|
||||
## 可选查询参数模型 { #optional-query-parameter-models }
|
||||
|
||||
声明查询参数模型时,使用 `| None` 并将默认值设为 `None`,即可使这组参数变为可选。
|
||||
|
||||
{* ../../docs_src/query_param_models/tutorial004_an_py310.py hl[18] *}
|
||||
|
||||
如果既未发送 `start` 也未发送 `end`,则 `date_range` 为 `None`。如果发送了其中任意一个,FastAPI 就会验证模型,并要求提供两个字段。
|
||||
|
||||
使用 `| None` 但不设置默认值时,该参数仍然是必需的。
|
||||
|
||||
额外参数遵循模型的 `extra` 配置。被忽略的额外参数不会使模型被视为已提供。
|
||||
|
||||
OpenAPI 会逐一列出这组参数的字段,并将它们标记为可选。
|
||||
|
||||
你也可以声明可选的 [header 参数模型](header-param-models.md#optional-header-parameter-models)和 [cookie 参数模型](cookie-param-models.md#optional-cookie-parameter-models)。
|
||||
|
||||
## 总结 { #summary }
|
||||
|
||||
你可以使用 **Pydantic 模型**在 **FastAPI** 中声明**查询参数**。😎
|
||||
|
||||
@@ -220,14 +220,22 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### 必填,但可以为 `None` { #required-can-be-none }
|
||||
### 必填参数与 `None` { #required-parameters-and-none }
|
||||
|
||||
你可以声明一个参数可以接收 `None`,但它仍然是必填的。这将强制客户端必须发送一个值,即使该值是 `None`。
|
||||
|
||||
为此,你可以声明 `None` 是有效类型,但不声明默认值:
|
||||
类型 `str | None` 允许使用 Python 值 `None`,但不会让查询参数变为可选。没有默认值时,该参数仍然是必填的:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
|
||||
查询参数值以字符串形式传入,没有内置的 `None` 表示方式。发送 `?q=None` 或 `?q=null` 分别传入的是字符串 `"None"` 和 `"null"`。发送 `?q=` 传入的是空字符串,在此示例中无法通过 `min_length=3` 校验。
|
||||
|
||||
要允许客户端省略 `q` 并使用 `None` 作为其值,请将其默认值设为 `None`:
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None, Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
与查询参数不同,JSON 请求体可以包含显式的 `null` 值。
|
||||
|
||||
## 查询参数列表 / 多个值 { #query-parameter-list-multiple-values }
|
||||
|
||||
当你用 `Query` 显式地定义查询参数时,你还可以声明它接收一个值列表,换句话说,接收多个值。
|
||||
|
||||
@@ -31,7 +31,7 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
|
||||
## 默认值 { #defaults }
|
||||
|
||||
查询参数不是路径的固定内容,它是可选的,还支持默认值。
|
||||
查询参数不是路径的固定内容,它可以是可选的,还支持默认值。
|
||||
|
||||
上例用 `skip=0` 和 `limit=10` 设定默认值。
|
||||
|
||||
@@ -108,14 +108,20 @@ http://127.0.0.1:8000/items/foo?short=on
|
||||
http://127.0.0.1:8000/items/foo?short=yes
|
||||
```
|
||||
|
||||
或其它任意大小写形式(大写、首字母大写等),函数接收的 `short` 参数都是布尔值 `True`。否则为 `False`。
|
||||
或其它任意大小写形式(大写、首字母大写等),函数接收的 `short` 参数都是布尔值 `True`。
|
||||
|
||||
`0`、`false`、`off` 和 `no` 等值(包括大小写变体)会转换为 `False`。
|
||||
|
||||
如果省略 `short`,**FastAPI** 会使用它的默认值,本例中为 `False`。
|
||||
|
||||
空值(`?short=`)或无法识别的值(例如 `?short=maybe`)会导致 `422` 校验错误。
|
||||
|
||||
|
||||
## 多个路径和查询参数 { #multiple-path-and-query-parameters }
|
||||
|
||||
**FastAPI** 可以识别同时声明的多个路径参数和查询参数。
|
||||
|
||||
而且声明查询参数的顺序并不重要。
|
||||
而且声明这些参数的顺序并不重要。
|
||||
|
||||
FastAPI 通过参数名进行检测:
|
||||
|
||||
@@ -139,7 +145,7 @@ FastAPI 通过参数名进行检测:
|
||||
http://127.0.0.1:8000/items/foo-item
|
||||
```
|
||||
|
||||
...因为路径中没有必选参数 `needy`,返回的响应中会显示如下错误信息:
|
||||
...因为没有添加必选参数 `needy`,返回的响应中会显示如下错误信息:
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
||||
@@ -28,6 +28,8 @@ $ uv add python-multipart
|
||||
|
||||
**FastAPI** 将从请求中的**表单数据**中**提取**出**每个字段**的数据,并提供你定义的 Pydantic 模型。
|
||||
|
||||
对于 [HTML 复选框](request-forms.md#boolean-form-fields),请将布尔字段的默认值声明为 `False`。
|
||||
|
||||
## 检查文档 { #check-the-docs }
|
||||
|
||||
你可以在文档 UI 中验证它,地址为 `/docs`:
|
||||
@@ -73,6 +75,58 @@ $ uv add python-multipart
|
||||
}
|
||||
```
|
||||
|
||||
## 组合表单模型 { #combine-form-models }
|
||||
|
||||
你可以将多个表单模型与单独的 `Form` 参数组合使用。
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial003_an_py310.py hl[9:14,19:21] *}
|
||||
|
||||
客户端将 `name`、`tags` 和 `token` 作为表单字段发送。每个模型独立验证表单字段。
|
||||
|
||||
每个模型的 `extra` 配置都适用于所有表单字段,包括在其他参数或依赖项中声明的字段和文件。使用 `extra="forbid"` 时,该模型之外的任何字段都会被拒绝。使用 `extra="allow"` 时,这些字段会作为模型的额外数据被包含进来。
|
||||
|
||||
如果多个声明使用相同的表单字段名,每个声明都会验证该字段的值。请为共享字段使用兼容的类型和约束,以便 OpenAPI schema 能一致地描述它们。
|
||||
|
||||
## 可选表单模型 { #optional-form-models }
|
||||
|
||||
使用 `| None` 并将默认值设为 `None` 来声明表单模型,即可使这组字段变为可选。
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial005_an_py310.py hl[17] *}
|
||||
|
||||
如果既未发送 `name` 也未发送 `age`,则 `profile` 为 `None`。如果发送了其中任意一个字段,FastAPI 就会验证模型,并要求提供这两个字段。提交空值也算作提供了字段。
|
||||
|
||||
使用 `| None` 但不设置默认值时,该参数仍然是必填的。
|
||||
|
||||
额外字段遵循模型的 `extra` 配置。被忽略的额外字段不会使模型被视为已提供。
|
||||
|
||||
仅包含未勾选复选框的可选模型会被省略。发送 `enabled=false` 这样的值可以显式提供该模型。
|
||||
|
||||
## 可选的表单模型联合类型 { #optional-unions-of-form-models }
|
||||
|
||||
你也可以使用可选的模型联合类型。
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial006_an_py310.py hl[9:16,21] *}
|
||||
|
||||
如果未发送任何表单字段,则 `contact` 为 `None`。发送 `kind=person` 和 `name`,或发送 `kind=company` 和 `company_name`。判别器会选择要验证的模型。
|
||||
|
||||
## 组合表单模型联合类型 { #combine-unions-of-form-models }
|
||||
|
||||
你可以将联合类型与其他表单模型、单独的 `Form` 参数以及 `File` 参数组合使用。每个联合类型都会独立验证。
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial007_an_py310.py hl[31:35] *}
|
||||
|
||||
在同一个表单中发送所有字段。例如,发送 `contact_type=person`、`name=Rick`、`notification_type=email`、`email=rick@example.com` 和 `token=secret`。
|
||||
|
||||
省略通知字段即可让 `notification` 保持为 `None`。
|
||||
|
||||
## 表单模型与文件 { #form-models-and-files }
|
||||
|
||||
你也可以将表单模型与[文件参数](request-files.md)组合使用。
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial004_an_py310.py hl[14] *}
|
||||
|
||||
这里,客户端使用 `multipart/form-data` 发送 `name` 字段和 `avatar` 文件。
|
||||
|
||||
## 总结 { #summary }
|
||||
|
||||
你可以使用 Pydantic 模型在 FastAPI 中声明表单字段。😎
|
||||
@@ -44,6 +44,14 @@ $ uv add python-multipart
|
||||
|
||||
///
|
||||
|
||||
## 布尔表单字段 { #boolean-form-fields }
|
||||
|
||||
浏览器在提交 HTML 表单时会忽略未选中的复选框。将布尔表单参数的默认值声明为 `False`,即可在复选框未选中时接收到 `False`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial002_an_py310.py hl[9] *}
|
||||
|
||||
选中的复选框会发送其值,默认值为 `"on"`。**FastAPI** 会将 `"on"` 解析为 `True`。
|
||||
|
||||
## 关于 "表单字段" { #about-form-fields }
|
||||
|
||||
HTML 表单(`<form></form>`)向服务器发送数据时通常会对数据使用一种“特殊”的编码方式,这与 JSON 不同。
|
||||
@@ -56,6 +64,8 @@ HTML 表单(`<form></form>`)向服务器发送数据时通常会对数据使
|
||||
|
||||
但当表单包含文件时,会编码为 `multipart/form-data`。你将在下一章阅读如何处理文件。
|
||||
|
||||
你可以使用 `Form(media_type="multipart/form-data")` 在 OpenAPI 中描述多部分表单。组合使用表单参数或模型时,只需在一个参数上设置即可。
|
||||
|
||||
如果你想了解更多关于这些编码和表单字段的信息,请参阅 [<abbr title="Mozilla Developer Network - Mozilla 开发者网络">MDN</abbr> Web 文档的 `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)。
|
||||
|
||||
///
|
||||
|
||||
@@ -42,7 +42,7 @@ FastAPI 会使用这个返回类型来:
|
||||
|
||||
/// note | 注意
|
||||
|
||||
注意,`response_model` 是「装饰器」方法(`get`、`post` 等)的一个参数。不是你的*路径操作函数*的参数,不像所有查询参数和请求体那样。
|
||||
注意,`response_model` 是「装饰器」方法(`get`、`post` 等)的一个参数。不是你的*路径操作函数*的参数,不像所有参数和请求体那样。
|
||||
|
||||
///
|
||||
|
||||
@@ -266,6 +266,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
||||
|
||||
但是,如果你的数据在具有默认值的模型字段中有实际的值,例如 ID 为 `bar` 的项:
|
||||
|
||||
<!-- fmt:off -->
|
||||
```Python hl_lines="3 5"
|
||||
{
|
||||
"name": "Bar",
|
||||
@@ -274,6 +275,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
||||
"tax": 20.2
|
||||
}
|
||||
```
|
||||
<!-- fmt:on -->
|
||||
|
||||
这些值将包含在响应中。
|
||||
|
||||
@@ -281,6 +283,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
||||
|
||||
如果数据具有与默认值相同的值,例如 ID 为 `baz` 的项:
|
||||
|
||||
<!-- fmt:off -->
|
||||
```Python hl_lines="3 5-6"
|
||||
{
|
||||
"name": "Baz",
|
||||
@@ -290,6 +293,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
||||
"tags": []
|
||||
}
|
||||
```
|
||||
<!-- fmt:on -->
|
||||
|
||||
FastAPI 足够聪明(实际上是 Pydantic 足够聪明)去认识到,即使 `description`、`tax` 和 `tags` 的值与默认值相同,它们也是被显式设置的(而不是取自默认值)。
|
||||
|
||||
|
||||
@@ -136,11 +136,11 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
disabled = user_dict["disabled"],
|
||||
hashed_password = user_dict["hashed_password"],
|
||||
username=user_dict["username"],
|
||||
email=user_dict["email"],
|
||||
full_name=user_dict["full_name"],
|
||||
disabled=user_dict["disabled"],
|
||||
hashed_password=user_dict["hashed_password"],
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user