diff --git a/docs/zh/docs/advanced/advanced-python-types.md b/docs/zh/docs/advanced/advanced-python-types.md index bbc9302e86..fdf87199c5 100644 --- a/docs/zh/docs/advanced/advanced-python-types.md +++ b/docs/zh/docs/advanced/advanced-python-types.md @@ -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`。 diff --git a/docs/zh/docs/advanced/async-tests.md b/docs/zh/docs/advanced/async-tests.md index 5ba7c6129b..445bbcad67 100644 --- a/docs/zh/docs/advanced/async-tests.md +++ b/docs/zh/docs/advanced/async-tests.md @@ -71,7 +71,7 @@ $ uv run pytest 这相当于: ```Python -response = client.get('/') +response = client.get("/") ``` ...我们曾经通过它向 `TestClient` 发出请求。 diff --git a/docs/zh/docs/advanced/behind-a-proxy.md b/docs/zh/docs/advanced/behind-a-proxy.md index bd99497b75..a4a1d0d74a 100644 --- a/docs/zh/docs/advanced/behind-a-proxy.md +++ b/docs/zh/docs/advanced/behind-a-proxy.md @@ -8,7 +8,7 @@ 在你的应用前面的**代理**通常会在把请求转发给你的**服务器**之前,临时设置一些请求头,让服务器知道该请求是由代理**转发**的,并告知原始(公网)URL,包括域名、是否使用 HTTPS 等。 -**服务器**程序(例如通过 **FastAPI CLI** 运行的 **Uvicorn**)能够解析这些请求头,然后把这些信息传递给你的应用。 +由 `fastapi run` 启动的**服务器**能够解析这些请求头,然后把这些信息传递给你的应用。 但出于安全考虑,由于服务器并不知道自己处在受信任的代理之后,它默认不会解析这些请求头。 @@ -34,8 +34,6 @@ ```console $ uv run fastapi run --forwarded-allow-ips="*" - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` @@ -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 - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -如果你使用 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: +然后,如果你这样启动服务器:
“我最近大量使用 FastAPI。我实际上计划把它用于我团队在 微软的机器学习(ML)服务。其中一些正在集成进核心 Windows 产品以及一些 Office 产品。”+
“我最近大量使用 FastAPI。我实际上计划把它用于我团队在 微软的所有机器学习(ML)服务。其中一些正在集成进核心 Windows 产品以及一些 Office 产品。”
kwargs。即使它们没有默认值。
+Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数都应该作为关键字参数(键值对)来调用,也被称为 kwargs。即使它们没有默认值。
{* ../../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] *}
diff --git a/docs/zh/docs/tutorial/query-param-models.md b/docs/zh/docs/tutorial/query-param-models.md
index fc691839d5..a3fdaeecab 100644
--- a/docs/zh/docs/tutorial/query-param-models.md
+++ b/docs/zh/docs/tutorial/query-param-models.md
@@ -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** 中声明**查询参数**。😎
diff --git a/docs/zh/docs/tutorial/query-params-str-validations.md b/docs/zh/docs/tutorial/query-params-str-validations.md
index 88d4306f0b..81405c47eb 100644
--- a/docs/zh/docs/tutorial/query-params-str-validations.md
+++ b/docs/zh/docs/tutorial/query-params-str-validations.md
@@ -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` 显式地定义查询参数时,你还可以声明它接收一个值列表,换句话说,接收多个值。
diff --git a/docs/zh/docs/tutorial/query-params.md b/docs/zh/docs/tutorial/query-params.md
index 971dbb0ede..02dda8dbdd 100644
--- a/docs/zh/docs/tutorial/query-params.md
+++ b/docs/zh/docs/tutorial/query-params.md
@@ -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
{
diff --git a/docs/zh/docs/tutorial/request-form-models.md b/docs/zh/docs/tutorial/request-form-models.md
index 9b3de06179..8fe4387c53 100644
--- a/docs/zh/docs/tutorial/request-form-models.md
+++ b/docs/zh/docs/tutorial/request-form-models.md
@@ -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 中声明表单字段。😎
diff --git a/docs/zh/docs/tutorial/request-forms.md b/docs/zh/docs/tutorial/request-forms.md
index 6da128a83f..a7e5150890 100644
--- a/docs/zh/docs/tutorial/request-forms.md
+++ b/docs/zh/docs/tutorial/request-forms.md
@@ -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 表单(``)向服务器发送数据时通常会对数据使用一种“特殊”的编码方式,这与 JSON 不同。
@@ -56,6 +64,8 @@ HTML 表单(``)向服务器发送数据时通常会对数据使
但当表单包含文件时,会编码为 `multipart/form-data`。你将在下一章阅读如何处理文件。
+你可以使用 `Form(media_type="multipart/form-data")` 在 OpenAPI 中描述多部分表单。组合使用表单参数或模型时,只需在一个参数上设置即可。
+
如果你想了解更多关于这些编码和表单字段的信息,请参阅 [MDN Web 文档的 `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)。
///
diff --git a/docs/zh/docs/tutorial/response-model.md b/docs/zh/docs/tutorial/response-model.md
index 5cdfee2176..afafe6bdfc 100644
--- a/docs/zh/docs/tutorial/response-model.md
+++ b/docs/zh/docs/tutorial/response-model.md
@@ -42,7 +42,7 @@ FastAPI 会使用这个返回类型来:
/// note | 注意
-注意,`response_model` 是「装饰器」方法(`get`、`post` 等)的一个参数。不是你的*路径操作函数*的参数,不像所有查询参数和请求体那样。
+注意,`response_model` 是「装饰器」方法(`get`、`post` 等)的一个参数。不是你的*路径操作函数*的参数,不像所有参数和请求体那样。
///
@@ -266,6 +266,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
但是,如果你的数据在具有默认值的模型字段中有实际的值,例如 ID 为 `bar` 的项:
+
```Python hl_lines="3 5"
{
"name": "Bar",
@@ -274,6 +275,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
"tax": 20.2
}
```
+
这些值将包含在响应中。
@@ -281,6 +283,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
如果数据具有与默认值相同的值,例如 ID 为 `baz` 的项:
+
```Python hl_lines="3 5-6"
{
"name": "Baz",
@@ -290,6 +293,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
"tags": []
}
```
+
FastAPI 足够聪明(实际上是 Pydantic 足够聪明)去认识到,即使 `description`、`tax` 和 `tags` 的值与默认值相同,它们也是被显式设置的(而不是取自默认值)。
diff --git a/docs/zh/docs/tutorial/security/simple-oauth2.md b/docs/zh/docs/tutorial/security/simple-oauth2.md
index 92cf02dd25..4721595b43 100644
--- a/docs/zh/docs/tutorial/security/simple-oauth2.md
+++ b/docs/zh/docs/tutorial/security/simple-oauth2.md
@@ -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"],
)
```