mirror of https://github.com/tiangolo/fastapi.git
🌐 Update translations for zh (update-all) (#14917)
* Update all * 🎨 Auto format * Add missing * 🎨 Auto format --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
149ef6264e
commit
eada361fa2
|
|
@ -0,0 +1,755 @@
|
|||
# LLM 测试文件 { #llm-test-file }
|
||||
|
||||
本文用于测试用于翻译文档的 <abbr title="Large Language Model - 大型语言模型">LLM</abbr> 是否理解 `scripts/translate.py` 中的 `general_prompt` 以及 `docs/{language code}/llm-prompt.md` 中的语言特定提示。语言特定提示会追加到 `general_prompt` 之后。
|
||||
|
||||
这里添加的测试会被所有语言特定提示的设计者看到。
|
||||
|
||||
用法如下:
|
||||
|
||||
* 准备语言特定提示——`docs/{language code}/llm-prompt.md`。
|
||||
* 将本文重新翻译为你的目标语言(例如使用 `translate.py` 的 `translate-page` 命令)。这会在 `docs/{language code}/docs/_llm-test.md` 下创建翻译。
|
||||
* 检查翻译是否正确。
|
||||
* 如有需要,改进你的语言特定提示、通用提示,或英文文档。
|
||||
* 然后手动修正翻译中剩余的问题,确保这是一个优秀的译文。
|
||||
* 重新翻译,在已有的优秀译文基础上进行。理想情况是 LLM 不再对译文做任何更改。这意味着通用提示和你的语言特定提示已经尽可能完善(有时它仍会做一些看似随机的改动,原因是<a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLM 不是确定性算法</a>)。
|
||||
|
||||
测试如下:
|
||||
|
||||
## 代码片段 { #code-snippets }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
这是一个代码片段:`foo`。这是另一个代码片段:`bar`。还有一个:`baz quux`。
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
代码片段的内容应保持不变。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### Content of code snippets` 部分。
|
||||
|
||||
////
|
||||
|
||||
## 引号 { #quotes }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
昨天,我的朋友写道:"如果你把 incorrectly 拼对了,你就把它拼错了"。我回答:"没错,但 'incorrectly' 错的不是 '"incorrectly"'"。
|
||||
|
||||
/// note | 注意
|
||||
|
||||
LLM 很可能会把这段翻错。我们只关心在重新翻译时它是否能保持修正后的译文。
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
提示词设计者可以选择是否将中性引号转换为排版引号。也可以保持不变。
|
||||
|
||||
例如参见 `docs/de/llm-prompt.md` 中的 `### Quotes` 部分。
|
||||
|
||||
////
|
||||
|
||||
## 代码片段中的引号 { #quotes-in-code-snippets }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
`pip install "foo[bar]"`
|
||||
|
||||
代码片段中的字符串字面量示例:`"this"`,`'that'`。
|
||||
|
||||
一个较难的字符串字面量示例:`f"I like {'oranges' if orange else "apples"}"`
|
||||
|
||||
硬核:`Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
... 但是,代码片段内的引号必须保持不变。
|
||||
|
||||
////
|
||||
|
||||
## 代码块 { #code-blocks }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
一个 Bash 代码示例...
|
||||
|
||||
```bash
|
||||
# 向宇宙打印问候
|
||||
echo "Hello universe"
|
||||
```
|
||||
|
||||
...以及一个控制台代码示例...
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
|
||||
Searching for package file structure
|
||||
```
|
||||
|
||||
...以及另一个控制台代码示例...
|
||||
|
||||
```console
|
||||
// 创建目录 "code"
|
||||
$ mkdir code
|
||||
// 切换到该目录
|
||||
$ cd code
|
||||
```
|
||||
|
||||
...以及一个 Python 代码示例...
|
||||
|
||||
```Python
|
||||
wont_work() # 这不会起作用 😱
|
||||
works(foo="bar") # 这可行 🎉
|
||||
```
|
||||
|
||||
...就这些。
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
代码块中的代码不应被修改,注释除外。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### Content of code blocks` 部分。
|
||||
|
||||
////
|
||||
|
||||
## 选项卡与彩色提示框 { #tabs-and-colored-boxes }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
/// info | 信息
|
||||
Some text
|
||||
///
|
||||
|
||||
/// note | 注意
|
||||
Some text
|
||||
///
|
||||
|
||||
/// note | 技术细节
|
||||
Some text
|
||||
///
|
||||
|
||||
/// check | 检查
|
||||
Some text
|
||||
///
|
||||
|
||||
/// tip | 提示
|
||||
Some text
|
||||
///
|
||||
|
||||
/// warning | 警告
|
||||
Some text
|
||||
///
|
||||
|
||||
/// danger | 危险
|
||||
Some text
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
选项卡以及 `Info`/`Note`/`Warning`/等提示块,应在竖线(`|`)后添加其标题的翻译。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### Special blocks` 与 `### Tab blocks` 部分。
|
||||
|
||||
////
|
||||
|
||||
## Web 与内部链接 { #web-and-internal-links }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
链接文本应被翻译,链接地址应保持不变:
|
||||
|
||||
* [链接到上面的标题](#code-snippets)
|
||||
* [内部链接](index.md#installation){.internal-link target=_blank}
|
||||
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">外部链接</a>
|
||||
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">样式链接</a>
|
||||
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">脚本链接</a>
|
||||
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">图片链接</a>
|
||||
|
||||
链接文本应被翻译,且链接地址应指向对应的译文页面:
|
||||
|
||||
* <a href="https://fastapi.tiangolo.com/zh/" class="external-link" target="_blank">FastAPI 链接</a>
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
链接的文本应被翻译,但地址保持不变。唯一的例外是指向 FastAPI 文档页面的绝对链接,此时应指向对应语言的译文。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### Links` 部分。
|
||||
|
||||
////
|
||||
|
||||
## HTML "abbr" 元素 { #html-abbr-elements }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
这里有一些包裹在 HTML "abbr" 元素中的内容(有些是虚构的):
|
||||
|
||||
### abbr 提供了完整短语 { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done - 尽管去做">GTD</abbr>
|
||||
* <abbr title="less than - 小于"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token - XML Web 令牌">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - 并行服务器网关接口">PSGI</abbr>
|
||||
|
||||
### abbr 提供了完整短语与解释 { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Mozilla 开发者网络: 为开发者编写的文档,由 Firefox 团队撰写">MDN</abbr>
|
||||
* <abbr title="Input/Output - 输入/输出: 磁盘读写,网络通信。">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
"abbr" 元素的 "title" 属性需要按照特定规则进行翻译。
|
||||
|
||||
译文可以自行添加 "abbr" 元素以解释英语单词,LLM 不应删除这些元素。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### HTML abbr elements` 部分。
|
||||
|
||||
////
|
||||
|
||||
## HTML "dfn" 元素 { #html-dfn-elements }
|
||||
|
||||
* <dfn title="配置为以某种方式连接并协同工作的机器组">集群</dfn>
|
||||
* <dfn title="一种使用具有多个隐藏层的人工神经网络的机器学习方法,从输入层到输出层构建了完整的内部结构">深度学习</dfn>
|
||||
|
||||
## 标题 { #headings }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
### 开发 Web 应用——教程 { #develop-a-webapp-a-tutorial }
|
||||
|
||||
Hello.
|
||||
|
||||
### 类型提示与注解 { #type-hints-and-annotations }
|
||||
|
||||
Hello again.
|
||||
|
||||
### 超类与子类 { #super-and-subclasses }
|
||||
|
||||
Hello again.
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
关于标题的唯一硬性规则是:LLM 必须保持花括号内的哈希部分不变,以确保链接不会失效。
|
||||
|
||||
参见 `scripts/translate.py` 中通用提示的 `### Headings` 部分。
|
||||
|
||||
语言特定的说明可参见例如 `docs/de/llm-prompt.md` 中的 `### Headings` 部分。
|
||||
|
||||
////
|
||||
|
||||
## 文档中使用的术语 { #terms-used-in-the-docs }
|
||||
|
||||
//// tab | 测试
|
||||
|
||||
* you
|
||||
* your
|
||||
|
||||
* e.g.
|
||||
* etc.
|
||||
|
||||
* `foo` as an `int`
|
||||
* `bar` as a `str`
|
||||
* `baz` as a `list`
|
||||
|
||||
* the Tutorial - User guide
|
||||
* the Advanced User Guide
|
||||
* the SQLModel docs
|
||||
* the API docs
|
||||
* the automatic docs
|
||||
|
||||
* Data Science
|
||||
* Deep Learning
|
||||
* Machine Learning
|
||||
* Dependency Injection
|
||||
* HTTP Basic authentication
|
||||
* HTTP Digest
|
||||
* ISO format
|
||||
* the JSON Schema standard
|
||||
* the JSON schema
|
||||
* the schema definition
|
||||
* Password Flow
|
||||
* Mobile
|
||||
|
||||
* deprecated
|
||||
* designed
|
||||
* invalid
|
||||
* on the fly
|
||||
* standard
|
||||
* default
|
||||
* case-sensitive
|
||||
* case-insensitive
|
||||
|
||||
* to serve the application
|
||||
* to serve the page
|
||||
|
||||
* the app
|
||||
* the application
|
||||
|
||||
* the request
|
||||
* the response
|
||||
* the error response
|
||||
|
||||
* the path operation
|
||||
* the path operation decorator
|
||||
* the path operation function
|
||||
|
||||
* the body
|
||||
* the request body
|
||||
* the response body
|
||||
* the JSON body
|
||||
* the form body
|
||||
* the file body
|
||||
* the function body
|
||||
|
||||
* the parameter
|
||||
* the body parameter
|
||||
* the path parameter
|
||||
* the query parameter
|
||||
* the cookie parameter
|
||||
* the header parameter
|
||||
* the form parameter
|
||||
* the function parameter
|
||||
|
||||
* the event
|
||||
* the startup event
|
||||
* the startup of the server
|
||||
* the shutdown event
|
||||
* the lifespan event
|
||||
|
||||
* the handler
|
||||
* the event handler
|
||||
* the exception handler
|
||||
* to handle
|
||||
|
||||
* the model
|
||||
* the Pydantic model
|
||||
* the data model
|
||||
* the database model
|
||||
* the form model
|
||||
* the model object
|
||||
|
||||
* the class
|
||||
* the base class
|
||||
* the parent class
|
||||
* the subclass
|
||||
* the child class
|
||||
* the sibling class
|
||||
* the class method
|
||||
|
||||
* the header
|
||||
* the headers
|
||||
* the authorization header
|
||||
* the `Authorization` header
|
||||
* the forwarded header
|
||||
|
||||
* the dependency injection system
|
||||
* the dependency
|
||||
* the dependable
|
||||
* the dependant
|
||||
|
||||
* I/O bound
|
||||
* CPU bound
|
||||
* concurrency
|
||||
* parallelism
|
||||
* multiprocessing
|
||||
|
||||
* the env var
|
||||
* the environment variable
|
||||
* the `PATH`
|
||||
* the `PATH` variable
|
||||
|
||||
* the authentication
|
||||
* the authentication provider
|
||||
* the authorization
|
||||
* the authorization form
|
||||
* the authorization provider
|
||||
* the user authenticates
|
||||
* the system authenticates the user
|
||||
|
||||
* the CLI
|
||||
* the command line interface
|
||||
|
||||
* the server
|
||||
* the client
|
||||
|
||||
* the cloud provider
|
||||
* the cloud service
|
||||
|
||||
* the development
|
||||
* the development stages
|
||||
|
||||
* the dict
|
||||
* the dictionary
|
||||
* the enumeration
|
||||
* the enum
|
||||
* the enum member
|
||||
|
||||
* the encoder
|
||||
* the decoder
|
||||
* to encode
|
||||
* to decode
|
||||
|
||||
* the exception
|
||||
* to raise
|
||||
|
||||
* the expression
|
||||
* the statement
|
||||
|
||||
* the frontend
|
||||
* the backend
|
||||
|
||||
* the GitHub discussion
|
||||
* the GitHub issue
|
||||
|
||||
* the performance
|
||||
* the performance optimization
|
||||
|
||||
* the return type
|
||||
* the return value
|
||||
|
||||
* the security
|
||||
* the security scheme
|
||||
|
||||
* the task
|
||||
* the background task
|
||||
* the task function
|
||||
|
||||
* the template
|
||||
* the template engine
|
||||
|
||||
* the type annotation
|
||||
* the type hint
|
||||
|
||||
* the server worker
|
||||
* the Uvicorn worker
|
||||
* the Gunicorn Worker
|
||||
* the worker process
|
||||
* the worker class
|
||||
* the workload
|
||||
|
||||
* the deployment
|
||||
* to deploy
|
||||
|
||||
* the SDK
|
||||
* the software development kit
|
||||
|
||||
* the `APIRouter`
|
||||
* the `requirements.txt`
|
||||
* the Bearer Token
|
||||
* the breaking change
|
||||
* the bug
|
||||
* the button
|
||||
* the callable
|
||||
* the code
|
||||
* the commit
|
||||
* the context manager
|
||||
* the coroutine
|
||||
* the database session
|
||||
* the disk
|
||||
* the domain
|
||||
* the engine
|
||||
* the fake X
|
||||
* the HTTP GET method
|
||||
* the item
|
||||
* the library
|
||||
* the lifespan
|
||||
* the lock
|
||||
* the middleware
|
||||
* the mobile application
|
||||
* the module
|
||||
* the mounting
|
||||
* the network
|
||||
* the origin
|
||||
* the override
|
||||
* the payload
|
||||
* the processor
|
||||
* the property
|
||||
* the proxy
|
||||
* the pull request
|
||||
* the query
|
||||
* the RAM
|
||||
* the remote machine
|
||||
* the status code
|
||||
* the string
|
||||
* the tag
|
||||
* the web framework
|
||||
* the wildcard
|
||||
* to return
|
||||
* to validate
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息
|
||||
|
||||
这是一份不完整且非规范性的(主要是)技术术语清单,取自文档中常见的词汇。它可能有助于提示词设计者判断哪些术语需要对 LLM 提供额外指引。例如当它总是把一个好的译法改回次优译法,或在你的语言中对某个术语的词形变化有困难时。
|
||||
|
||||
参见例如 `docs/de/llm-prompt.md` 中的 `### List of English terms and their preferred German translations` 部分。
|
||||
|
||||
////
|
||||
|
||||
////
|
||||
|
||||
翻译(术语)对照:
|
||||
|
||||
//// tab | 测试(译文)
|
||||
|
||||
* 你
|
||||
* 你的
|
||||
|
||||
* 例如
|
||||
* 等等
|
||||
|
||||
* 将 `foo` 作为 `int`
|
||||
* 将 `bar` 作为 `str`
|
||||
* 将 `baz` 作为 `list`
|
||||
|
||||
* 教程 - 用户指南
|
||||
* 高级用户指南
|
||||
* SQLModel 文档
|
||||
* API 文档
|
||||
* 自动文档
|
||||
|
||||
* 数据科学
|
||||
* 深度学习
|
||||
* 机器学习
|
||||
* 依赖注入
|
||||
* HTTP 基本认证
|
||||
* HTTP 摘要认证
|
||||
* ISO 格式
|
||||
* JSON Schema 标准
|
||||
* JSON 模式
|
||||
* 模式定义
|
||||
* 密码流
|
||||
* 移动端
|
||||
|
||||
* 已弃用
|
||||
* 设计的
|
||||
* 无效
|
||||
* 即时
|
||||
* 标准的
|
||||
* 默认的
|
||||
* 区分大小写
|
||||
* 不区分大小写
|
||||
|
||||
* 为应用提供服务
|
||||
* 为页面提供服务
|
||||
|
||||
* 应用
|
||||
* 应用程序
|
||||
|
||||
* 请求
|
||||
* 响应
|
||||
* 错误响应
|
||||
|
||||
* 路径操作
|
||||
* 路径操作装饰器
|
||||
* 路径操作函数
|
||||
|
||||
* 主体
|
||||
* 请求体
|
||||
* 响应体
|
||||
* JSON 体
|
||||
* 表单体
|
||||
* 文件体
|
||||
* 函数体
|
||||
|
||||
* 参数
|
||||
* 请求体参数
|
||||
* 路径参数
|
||||
* 查询参数
|
||||
* Cookie 参数
|
||||
* Header 参数
|
||||
* 表单参数
|
||||
* 函数参数
|
||||
|
||||
* 事件
|
||||
* 启动事件
|
||||
* 服务器的启动
|
||||
* 关闭事件
|
||||
* 生命周期事件
|
||||
|
||||
* 处理器
|
||||
* 事件处理器
|
||||
* 异常处理器
|
||||
* 处理
|
||||
|
||||
* 模型
|
||||
* Pydantic 模型
|
||||
* 数据模型
|
||||
* 数据库模型
|
||||
* 表单模型
|
||||
* 模型对象
|
||||
|
||||
* 类
|
||||
* 基类
|
||||
* 父类
|
||||
* 子类
|
||||
* 子类
|
||||
* 兄弟类
|
||||
* 类方法
|
||||
|
||||
* 请求头
|
||||
* 请求头
|
||||
* 授权头
|
||||
* `Authorization` 头
|
||||
* 转发头
|
||||
|
||||
* 依赖注入系统
|
||||
* 依赖
|
||||
* 可依赖对象
|
||||
* 依赖项
|
||||
|
||||
* I/O 受限
|
||||
* CPU 受限
|
||||
* 并发
|
||||
* 并行
|
||||
* 多进程
|
||||
|
||||
* 环境变量
|
||||
* 环境变量
|
||||
* `PATH`
|
||||
* `PATH` 变量
|
||||
|
||||
* 认证
|
||||
* 认证提供方
|
||||
* 授权
|
||||
* 授权表单
|
||||
* 授权提供方
|
||||
* 用户进行认证
|
||||
* 系统对用户进行认证
|
||||
|
||||
* CLI
|
||||
* 命令行界面
|
||||
|
||||
* 服务器
|
||||
* 客户端
|
||||
|
||||
* 云服务提供商
|
||||
* 云服务
|
||||
|
||||
* 开发
|
||||
* 开发阶段
|
||||
|
||||
* dict
|
||||
* 字典
|
||||
* 枚举
|
||||
* 枚举
|
||||
* 枚举成员
|
||||
|
||||
* 编码器
|
||||
* 解码器
|
||||
* 编码
|
||||
* 解码
|
||||
|
||||
* 异常
|
||||
* 抛出
|
||||
|
||||
* 表达式
|
||||
* 语句
|
||||
|
||||
* 前端
|
||||
* 后端
|
||||
|
||||
* GitHub 讨论
|
||||
* GitHub Issue
|
||||
|
||||
* 性能
|
||||
* 性能优化
|
||||
|
||||
* 返回类型
|
||||
* 返回值
|
||||
|
||||
* 安全
|
||||
* 安全方案
|
||||
|
||||
* 任务
|
||||
* 后台任务
|
||||
* 任务函数
|
||||
|
||||
* 模板
|
||||
* 模板引擎
|
||||
|
||||
* 类型注解
|
||||
* 类型提示
|
||||
|
||||
* 服务器 worker
|
||||
* Uvicorn worker
|
||||
* Gunicorn worker
|
||||
* worker 进程
|
||||
* worker 类
|
||||
* 工作负载
|
||||
|
||||
* 部署
|
||||
* 部署
|
||||
|
||||
* SDK
|
||||
* 软件开发工具包
|
||||
|
||||
* `APIRouter`
|
||||
* `requirements.txt`
|
||||
* Bearer Token
|
||||
* 破坏性变更
|
||||
* Bug
|
||||
* 按钮
|
||||
* 可调用对象
|
||||
* 代码
|
||||
* 提交
|
||||
* 上下文管理器
|
||||
* 协程
|
||||
* 数据库会话
|
||||
* 磁盘
|
||||
* 域名
|
||||
* 引擎
|
||||
* 假 X
|
||||
* HTTP GET 方法
|
||||
* 项
|
||||
* 库
|
||||
* 生命周期
|
||||
* 锁
|
||||
* 中间件
|
||||
* 移动应用
|
||||
* 模块
|
||||
* 挂载
|
||||
* 网络
|
||||
* 源
|
||||
* 覆盖
|
||||
* 负载
|
||||
* 处理器
|
||||
* 属性
|
||||
* 代理
|
||||
* Pull Request
|
||||
* 查询
|
||||
* RAM
|
||||
* 远程机器
|
||||
* 状态码
|
||||
* 字符串
|
||||
* 标签
|
||||
* Web 框架
|
||||
* 通配符
|
||||
* 返回
|
||||
* 校验
|
||||
|
||||
////
|
||||
|
||||
//// tab | 信息(译文)
|
||||
|
||||
此清单是不完整且非规范性的,列出(主要是)文档中出现的技术术语。它有助于提示词设计者确定哪些术语需要额外的指引。例如当 LLM 总是把更好的译法改回次优译法,或在你的语言中难以正确变形时。
|
||||
|
||||
也可参见 `docs/de/llm-prompt.md` 中的 `### List of English terms and their preferred German translations` 部分。
|
||||
|
||||
////
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# 关于 { #about }
|
||||
|
||||
关于 FastAPI、其设计、灵感等。🤓
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
例如,要声明另一个状态码为 `404` 且具有 Pydantic 模型 `Message` 的响应,你可以这样写:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
@ -203,7 +203,7 @@
|
|||
|
||||
以及一个状态码为 `200` 的响应,它使用你的 `response_model`,但包含自定义的 `example`:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
所有这些都会被合并并包含到你的 OpenAPI 中,并显示在 API 文档里:
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
|
||||
|
||||
/// warning
|
||||
/// warning | 警告
|
||||
|
||||
当你直接返回一个像上面例子中的 `Response` 对象时,它会直接返回。
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
///
|
||||
|
||||
/// note | 技术细节
|
||||
/// note | 注意
|
||||
|
||||
你也可以使用 `from starlette.responses import JSONResponse`。
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
为此,声明一个 `__call__` 方法:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
在这种情况下,**FastAPI** 会使用这个 `__call__` 来检查附加参数和子依赖,并且稍后会调用它,把返回值传递给你的*路径操作函数*中的参数。
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
现在,我们可以用 `__init__` 声明实例的参数,用来“参数化”这个依赖项:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
在本例中,**FastAPI** 不会接触或关心 `__init__`,我们会在自己的代码中直接使用它。
|
||||
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
我们可以这样创建该类的实例:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
这样就把依赖项“参数化”了,现在它内部带有属性 `checker.fixed_content` 的值 `"bar"`。
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ checker(q="somequery")
|
|||
|
||||
...并将其返回值作为依赖项的值,传给我们的*路径操作函数*中的参数 `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# 高级 Python 类型 { #advanced-python-types }
|
||||
|
||||
这里有一些在使用 Python 类型时可能有用的额外想法。
|
||||
|
||||
## 使用 `Union` 或 `Optional` { #using-union-or-optional }
|
||||
|
||||
如果你的代码因为某些原因不能使用 `|`,例如它不是在类型注解里,而是在 `response_model=` 之类的参数中,那么你可以使用 `typing` 中的 `Union` 来代替竖线(`|`)。
|
||||
|
||||
例如,你可以声明某个值可以是 `str` 或 `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` 也提供了一个声明“可能为 `None`”的快捷方式:`Optional`。
|
||||
|
||||
从我非常主观的角度给个小建议:
|
||||
|
||||
- 🚨 避免使用 `Optional[SomeType]`
|
||||
- 改用 ✨`Union[SomeType, None]`✨。
|
||||
|
||||
两者是等价的,底层其实也是一样的。但我更推荐使用 `Union` 而不是 `Optional`,因为单词“optional”(可选)看起来会暗示该值是可选的,而它真正的含义是“它可以是 `None`”,即使它并不是可选的,仍然是必填的。
|
||||
|
||||
我认为 `Union[SomeType, None]` 更能明确表达其含义。
|
||||
|
||||
这只是关于词语和命名的问题,但这些词语会影响你和你的队友如何看待代码。
|
||||
|
||||
举个例子,看这段函数:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
参数 `name` 被定义为 `Optional[str]`,但它并不是“可选”的,你不能不传这个参数就调用函数:
|
||||
|
||||
```Python
|
||||
say_hi() # 哎呀,这会报错!😱
|
||||
```
|
||||
|
||||
参数 `name` 仍然是必填的(不是“可选”),因为它没有默认值。不过,`name` 接受 `None` 作为取值:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # 这样可以,None 是有效的 🎉
|
||||
```
|
||||
|
||||
好消息是,在大多数情况下,你可以直接使用 `|` 来定义类型联合:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
因此,通常你不必为像 `Optional` 和 `Union` 这样的名字而操心。😎
|
||||
|
|
@ -32,11 +32,11 @@
|
|||
|
||||
文件 `main.py` 将包含:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
文件 `test_main.py` 将包含针对 `main.py` 的测试,现在它可能看起来如下:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## 运行测试 { #run-it }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ $ pytest
|
|||
|
||||
这个标记 `@pytest.mark.anyio` 会告诉 pytest 该测试函数应该被异步调用:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ $ pytest
|
|||
|
||||
我们现在可以使用应用程序创建一个 `AsyncClient` ,并使用 `await` 向其发送异步请求。
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
这相当于:
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ $ pytest
|
|||
response = client.get('/')
|
||||
```
|
||||
|
||||
我们曾经通过它向 `TestClient` 发出请求。
|
||||
...我们曾经通过它向 `TestClient` 发出请求。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
|||
|
||||
例如,假设你定义了一个*路径操作* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
如果客户端尝试访问 `/items`,默认会被重定向到 `/items/`。
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ sequenceDiagram
|
|||
|
||||
即使你的所有代码都假设只有 `/app`。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
代理会在将请求传递给应用服务器(可能是通过 FastAPI CLI 运行的 Uvicorn)之前,实时**“移除”**这个**路径前缀**,让你的应用认为它是在 `/app` 被服务,这样你就不需要更新所有代码去包含 `/api/v1` 前缀。
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ ASGI 规范为这种用例定义了 `root_path`。
|
|||
|
||||
这里我们把它包含在响应消息中仅用于演示。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
然后,如果你这样启动 Uvicorn:
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
|||
|
||||
或者,如果你无法提供类似 `--root-path` 的命令行选项,你可以在创建 FastAPI 应用时设置参数 `root_path`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
把 `root_path` 传给 `FastAPI` 等同于把命令行选项 `--root-path` 传给 Uvicorn 或 Hypercorn。
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
|||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
会生成如下的 OpenAPI 模式:
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ OpenAPI 规范中的 `servers` 属性是可选的。
|
|||
|
||||
如果你不希望 **FastAPI** 包含一个使用 `root_path` 的自动服务器,可以使用参数 `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
这样它就不会被包含到 OpenAPI 模式中。
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
但如果你确定你返回的内容是「可以用 JSON 序列化」的,你可以将它直接传给响应类,从而避免在传给响应类之前先通过 `jsonable_encoder` 带来的额外开销。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
* 导入 `HTMLResponse`。
|
||||
* 将 `HTMLResponse` 作为你的 *路径操作* 的 `response_class` 参数传入。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
|
||||
和上面一样的例子,返回一个 `HTMLResponse` 看起来可能是这样:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
|
||||
比如像这样:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
在这个例子中,函数 `generate_html_response()` 已经生成并返回 `Response` 对象而不是在 `str` 中返回 HTML。
|
||||
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
|
||||
FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 `media_type` 的 Content-Type 头,并为文本类型附加一个字符集。
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
接受文本或字节并返回纯文本响应。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -194,13 +194,13 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
你可以直接返回一个 `RedirectResponse`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
或者你可以把它用于 `response_class` 参数:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
如果你这么做,那么你可以在 *路径操作* 函数中直接返回 URL。
|
||||
|
||||
|
|
@ -210,13 +210,13 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
你也可以将 `status_code` 参数和 `response_class` 参数结合使用:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
采用异步生成器或普通生成器/迭代器,然后流式传输响应主体。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### 对类似文件的对象使用 `StreamingResponse` { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
这也包括许多与云存储、视频处理等交互的库。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. 这是生成器函数。之所以是「生成器函数」,是因为它内部包含 `yield` 语句。
|
||||
2. 通过使用 `with` 代码块,我们可以确保在生成器函数完成后关闭类文件对象。因此,在它完成发送响应之后会被关闭。
|
||||
|
|
@ -255,11 +255,11 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
文件响应将包含适当的 `Content-Length`、`Last-Modified` 和 `ETag` 响应头。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
你也可以使用 `response_class` 参数:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
在这种情况下,你可以在 *路径操作* 函数中直接返回文件路径。
|
||||
|
||||
|
|
@ -273,7 +273,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
你可以创建一个 `CustomORJSONResponse`。你需要做的主要事情是实现一个返回 `bytes` 的 `Response.render(content)` 方法:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
现在,不再是返回:
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
…这个响应将返回:
|
||||
...这个响应将返回:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -299,7 +299,7 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
|||
|
||||
在下面的示例中,**FastAPI** 会在所有 *路径操作* 中默认使用 `ORJSONResponse`,而不是 `JSONResponse`。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ FastAPI 基于 **Pydantic** 构建,我已经向你展示过如何使用 Pydant
|
|||
在本例中,它是一个 `Item` 数据类列表。
|
||||
6. 这里我们返回一个字典,里面的 `items` 是一个数据类列表。
|
||||
|
||||
FastAPI 仍然能够将数据<abbr title="把数据转换为可以传输的格式">序列化</abbr>为 JSON。
|
||||
FastAPI 仍然能够将数据<dfn title="把数据转换为可以传输的格式">序列化</dfn>为 JSON。
|
||||
7. 这里的 `response_model` 使用了 “`Author` 数据类列表” 的类型注解。
|
||||
|
||||
同样,你可以将 `dataclasses` 与标准类型注解组合使用。
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
我们使用 `yield` 创建了一个异步函数 `lifespan()` 像这样:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。
|
||||
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
这个函数在 `yield` 之前的部分,会在应用启动前执行。
|
||||
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
它将函数转化为所谓的“**异步上下文管理器**”。
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
在 Python 中,**上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ async with lifespan(app):
|
|||
|
||||
`FastAPI` 的 `lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的异步上下文管理器 `lifespan` 传给它。
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## 替代事件(弃用) { #alternative-events-deprecated }
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ async with lifespan(app):
|
|||
|
||||
使用事件 `"startup"` 声明一个在应用启动前运行的函数:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
本例中,`startup` 事件处理器函数为项目“数据库”(只是一个 `dict`)提供了一些初始值。
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ async with lifespan(app):
|
|||
|
||||
使用事件 `"shutdown"` 声明一个在应用关闭时运行的函数:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
此处,`shutdown` 事件处理器函数会向文件 `log.txt` 写入一行文本 `"Application shutdown"`。
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ FastAPI 会自动生成 **OpenAPI 3.1** 规范,因此你使用的任何工具
|
|||
|
||||
先从一个简单的 FastAPI 应用开始:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
请注意,这些*路径操作*使用 `Item` 和 `ResponseMessage` 模型来定义它们的请求载荷和响应载荷。
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
|||
|
||||
例如,你可以有一个 **items** 相关的部分和另一个 **users** 相关的部分,它们可以用标签来分隔:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### 生成带标签的 TypeScript 客户端 { #generate-a-typescript-client-with-tags }
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
|||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
|
||||
```
|
||||
|
||||
……这是因为客户端生成器会把每个*路径操作*的 OpenAPI 内部**操作 ID(operation ID)**用作方法名的一部分。
|
||||
...这是因为客户端生成器会把每个*路径操作*的 OpenAPI 内部**操作 ID(operation ID)**用作方法名的一部分。
|
||||
|
||||
OpenAPI 要求每个操作 ID 在所有*路径操作*中都是唯一的,因此 FastAPI 会使用**函数名**、**路径**和**HTTP 方法/操作**来生成操作 ID,以确保其唯一性。
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ FastAPI 为每个*路径操作*使用一个**唯一 ID**,它既用于**操作
|
|||
|
||||
然后你可以把这个自定义函数通过 `generate_unique_id_function` 参数传给 **FastAPI**:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### 使用自定义操作 ID 生成 TypeScript 客户端 { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ FastAPI 为每个*路径操作*使用一个**唯一 ID**,它既用于**操作
|
|||
|
||||
我们可以把 OpenAPI JSON 下载到 `openapi.json` 文件中,然后用如下脚本**移除这个标签前缀**:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
|||
|
||||
**FastAPI** 为常见用例提供了一些中间件,下面介绍怎么使用这些中间件。
|
||||
|
||||
/// note | 注意
|
||||
/// note | 技术细节
|
||||
|
||||
以下几个示例中也可以使用 `from starlette.middleware.something import SomethingMiddleware`。
|
||||
|
||||
|
|
@ -57,13 +57,13 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
|||
|
||||
任何传向 `http` 或 `ws` 的请求都会被重定向至安全方案。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
强制所有传入请求都必须正确设置 `Host` 请求头,以防 HTTP 主机头攻击。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
支持以下参数:
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
|||
|
||||
中间件会处理标准响应与流响应。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
支持以下参数:
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ JSON 请求体包含如下内容:
|
|||
|
||||
### 查看文档 { #check-the-docs }
|
||||
|
||||
现在,使用 Uvicorn 启动应用,打开 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
|
||||
现在,启动应用并打开 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
|
||||
|
||||
就能看到文档的*路径操作*已经包含了**回调**的内容以及*外部 API*:
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
当您创建一个 **FastAPI** 应用程序时,有一个 `webhooks` 属性可以用来定义网络钩子,方式与您定义*路径操作*的时候相同,例如使用 `@app.webhooks.post()` 。
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
|
||||
|
||||
您定义的网络钩子将被包含在 `OpenAPI` 的架构中,并出现在自动生成的**文档 UI** 中。
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
务必确保每个操作的 `operation_id` 都是唯一的。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### 使用 *路径操作函数* 的函数名作为 operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
你应该在添加了所有 *路径操作* 之后执行此操作。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
使用参数 `include_in_schema` 并将其设置为 `False`,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## 来自 docstring 的高级描述 { #advanced-description-from-docstring }
|
||||
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
|
||||
例如,这个 `openapi_extra` 可用于声明 [OpenAPI 扩展](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
当你打开自动 API 文档时,你的扩展会显示在该 *路径操作* 的底部。
|
||||
|
||||
|
|
@ -139,9 +139,9 @@
|
|||
|
||||
你可以用 `openapi_extra` 来做到:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
在这个示例中,我们没有声明任何 Pydantic 模型。事实上,请求体甚至没有被 <abbr title="从某种纯文本格式(如字节)转换为 Python 对象">解析</abbr> 为 JSON,而是直接以 `bytes` 读取,并由函数 `magic_data_reader()` 以某种方式负责解析。
|
||||
在这个示例中,我们没有声明任何 Pydantic 模型。事实上,请求体甚至没有被 <dfn title="从某种纯文本格式(如字节)转换为 Python 对象">解析</dfn> 为 JSON,而是直接以 `bytes` 读取,并由函数 `magic_data_reader()` 以某种方式负责解析。
|
||||
|
||||
尽管如此,我们仍然可以声明请求体的预期方案。
|
||||
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
|
||||
例如,在这个应用中我们不使用 FastAPI 集成的从 Pydantic 模型提取 JSON Schema 的功能,也不使用对 JSON 的自动校验。实际上,我们将请求的内容类型声明为 YAML,而不是 JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
尽管我们没有使用默认的集成功能,我们仍然使用 Pydantic 模型手动生成我们想以 YAML 接收的数据的 JSON Schema。
|
||||
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
|
||||
接着在我们的代码中,我们直接解析该 YAML 内容,然后再次使用同一个 Pydantic 模型来验证该 YAML 内容:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@
|
|||
|
||||
然后你可以在这个*临时*响应对象中设置`status_code`。
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
|
||||
|
||||
然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。
|
||||
然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。
|
||||
|
||||
如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。
|
||||
|
||||
**FastAPI**将使用这个*临时*响应来提取状态码(也包括cookies和头部),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
你可以在 *路径操作函数* 中定义一个类型为 `Response` 的参数,这样你就可以在这个临时响应对象中设置cookie了。
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
|
||||
|
||||
而且你还可以根据你的需要响应不同的对象,比如常用的 `dict`,数据库model等。
|
||||
|
||||
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
然后设置Cookies,并返回:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
需要注意,如果你直接反馈一个response对象,而不是使用`Response`入参,FastAPI则会直接反馈你封装的response对象。
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
|
||||
|
||||
/// note | 注意
|
||||
/// note | 技术细节
|
||||
|
||||
你也可以使用 `from starlette.responses import JSONResponse`。
|
||||
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
你可以把你的 XML 内容放到一个字符串中,放到一个 `Response` 中,然后返回:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## 说明 { #notes }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
然后你可以在这个*临时*响应对象中设置头部。
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
|
||||
|
||||
然后你可以像平常一样返回任何你需要的对象(例如一个 `dict` 或者一个数据库模型)。
|
||||
|
||||
如果你声明了一个 `response_model`,它仍然会被用来过滤和转换你返回的对象。
|
||||
|
||||
**FastAPI** 将使用这个临时响应来提取头部(也包括 cookies 和状态码),并将它们放入包含你返回的值的最终响应中,该响应由任何 `response_model` 过滤。
|
||||
**FastAPI** 将使用这个*临时*响应来提取头部(也包括 cookies 和状态码),并将它们放入包含你返回的值的最终响应中,该响应由任何 `response_model` 过滤。
|
||||
|
||||
你也可以在依赖项中声明 `Response` 参数,并在其中设置头部(和 cookies)。
|
||||
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
按照[直接返回响应](response-directly.md){.internal-link target=_blank}中所述创建响应,并将头部作为附加参数传递:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
|
|||
* 返回类型为 `HTTPBasicCredentials` 的对象:
|
||||
* 包含发送的 `username` 与 `password`
|
||||
|
||||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||||
|
||||
第一次打开 URL(或在 API 文档中点击 **Execute** 按钮)时,浏览器要求输入用户名与密码:
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ HTTP 基础授权让浏览器显示内置的用户名与密码提示。
|
|||
|
||||
然后我们可以使用 `secrets.compare_digest()` 来确保 `credentials.username` 是 `"stanleyjobson"`,且 `credentials.password` 是`"swordfish"`。
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||||
|
||||
这类似于:
|
||||
|
||||
|
|
@ -104,4 +104,4 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
|||
|
||||
检测到凭证不正确后,返回 `HTTPException` 及状态码 401(与无凭证时返回的内容一样),并添加响应头 `WWW-Authenticate`,让浏览器再次显示登录提示:
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ $ pip install "fastapi[all]"
|
|||
|
||||
你可以使用与 Pydantic 模型相同的验证功能和工具,例如不同的数据类型,以及使用 `Field()` 进行附加验证。
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ $ pip install "fastapi[all]"
|
|||
|
||||
然后你可以在应用中使用新的 `settings` 对象:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
|
||||
|
||||
### 运行服务器 { #run-the-server }
|
||||
|
||||
|
|
@ -100,19 +100,19 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
|||
|
||||
## 在另一个模块中放置设置 { #settings-in-another-module }
|
||||
|
||||
你可以把这些设置放在另一个模块文件中,就像你在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。
|
||||
你可以把这些设置放在另一个模块文件中,就像你在[更大的应用 - 多个文件](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。
|
||||
|
||||
例如,可以有一个 `config.py` 文件:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
{* ../../docs_src/settings/app01_py310/config.py *}
|
||||
|
||||
然后在 `main.py` 文件中使用它:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
你还需要一个 `__init__.py` 文件,就像你在[Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。
|
||||
你还需要一个 `__init__.py` 文件,就像你在[更大的应用 - 多个文件](../tutorial/bigger-applications.md){.internal-link target=_blank}中看到的那样。
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
|||
|
||||
延续上一个示例,你的 `config.py` 文件可能如下所示:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
|
||||
|
||||
注意,现在我们不再创建默认实例 `settings = Settings()`。
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
|||
|
||||
现在我们创建一个依赖项,返回一个新的 `config.Settings()`。
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -144,15 +144,15 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
|||
|
||||
///
|
||||
|
||||
然后我们可以在“路径操作函数”中将其作为依赖项引入,并在需要的任何地方使用它。
|
||||
然后我们可以在路径操作函数中将其作为依赖项引入,并在需要的任何地方使用它。
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
|
||||
|
||||
### 设置与测试 { #settings-and-testing }
|
||||
|
||||
接着,在测试期间,通过为 `get_settings` 创建依赖项覆盖,就可以很容易地提供一个不同的设置对象:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
在依赖项覆盖中,我们在创建新的 `Settings` 对象时为 `admin_email` 设置了一个新值,然后返回该新对象。
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ APP_NAME="ChimichangApp"
|
|||
|
||||
然后更新 `config.py`:
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ def get_settings():
|
|||
|
||||
但由于我们在顶部使用了 `@lru_cache` 装饰器,`Settings` 对象只会在第一次调用时创建一次。 ✔️
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
|
||||
|
||||
接着,对于后续请求中依赖项里对 `get_settings()` 的任何调用,它不会再次执行 `get_settings()` 的内部代码并创建新的 `Settings` 对象,而是会一遍又一遍地返回第一次调用时返回的那个相同对象。
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
首先,创建主(顶层)**FastAPI** 应用及其*路径操作*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
|
||||
|
||||
### 子应用 { #sub-application }
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
子应用只是另一个标准 FastAPI 应用,但这个应用是被**挂载**的应用:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
|
||||
|
||||
### 挂载子应用 { #mount-the-sub-application }
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
本例的子应用挂载在 `/subapi` 路径下:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
|
||||
|
||||
### 查看自动 API 文档 { #check-the-automatic-api-docs }
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ $ pip install jinja2
|
|||
* 在返回模板的*路径操作*中声明 `Request` 参数
|
||||
* 使用 `templates` 渲染并返回 `TemplateResponse`,传递模板的名称、request 对象以及一个包含多个键值对(用于 Jinja2 模板)的 "context" 字典。
|
||||
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
|
||||
|
||||
/// note
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ FastAPI 可以覆盖这些位置的依赖项。
|
|||
app.dependency_overrides = {}
|
||||
```
|
||||
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
如果只在某些测试时覆盖依赖项,您可以在测试开始时(在测试函数内)设置覆盖依赖项,并在结束时(在测试函数结尾)重置覆盖依赖项。
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
当你需要在测试中运行 `lifespan` 时,可以将 `TestClient` 与 `with` 语句一起使用:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
你可以在[官方 Starlette 文档站点的“在测试中运行 lifespan”](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)阅读更多细节。
|
||||
|
||||
对于已弃用的 `startup` 和 `shutdown` 事件,可以按如下方式使用 `TestClient`:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
为此,在 `with` 语句中使用 `TestClient` 连接到 WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
此时,需要直接访问请求。
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
|
||||
|
||||
把*路径操作函数*的参数类型声明为 `Request`,**FastAPI** 就能把 `Request` 传递到参数里。
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ $ pip install websockets
|
|||
|
||||
但这是一种专注于 WebSockets 的服务器端并提供一个工作示例的最简单方式:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
|
||||
|
||||
## 创建 `websocket` { #create-a-websocket }
|
||||
|
||||
在您的 **FastAPI** 应用程序中,创建一个 `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ $ pip install websockets
|
|||
|
||||
在您的 WebSocket 路由中,您可以使用 `await` 等待消息并发送消息。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
|
||||
|
||||
您可以接收和发送二进制、文本和 JSON 数据。
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ $ fastapi dev main.py
|
|||
|
||||
当 WebSocket 连接关闭时,`await websocket.receive_text()` 将引发 `WebSocketDisconnect` 异常,您可以捕获并处理该异常,就像本示例中的示例一样。
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
|
||||
|
||||
尝试以下操作:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
之后将其挂载到某一个路径下。
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,482 @@
|
|||
# 替代方案、灵感与对比 { #alternatives-inspiration-and-comparisons }
|
||||
|
||||
是什么启发了 **FastAPI**,它与替代方案的比较,以及它从中学到的东西。
|
||||
|
||||
## 介绍 { #intro }
|
||||
|
||||
没有前人的工作,就不会有 **FastAPI**。
|
||||
|
||||
在它诞生之前,已经有许多工具为其提供了灵感。
|
||||
|
||||
我曾经多年避免创建一个新框架。起初,我尝试用许多不同的框架、插件和工具来解决 **FastAPI** 所覆盖的全部功能。
|
||||
|
||||
但在某个时刻,除了创造一个能提供所有这些功能的东西之外,别无选择;它要吸收以往工具的最佳理念,并以尽可能好的方式组合起来,利用之前都不存在的语言特性(Python 3.6+ 类型提示)。
|
||||
|
||||
## 先前的工具 { #previous-tools }
|
||||
|
||||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
|
||||
|
||||
它是最流行且被广泛信任的 Python 框架。被用于构建 Instagram 等系统。
|
||||
|
||||
它与关系型数据库(如 MySQL、PostgreSQL)耦合相对紧密,因此若要以 NoSQL 数据库(如 Couchbase、MongoDB、Cassandra 等)作为主要存储引擎并不容易。
|
||||
|
||||
它最初用于在后端生成 HTML,而不是创建由现代前端(如 React、Vue.js、Angular)或与之通信的其他系统(如 <abbr title="Internet of Things - 物联网">IoT</abbr> 设备)使用的 API。
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
|
||||
|
||||
Django REST framework 作为一个灵活工具箱而创建,用于在底层使用 Django 构建 Web API,从而增强其 API 能力。
|
||||
|
||||
它被包括 Mozilla、Red Hat、Eventbrite 在内的许多公司使用。
|
||||
|
||||
它是最早的“自动 API 文档”的范例之一,这正是启发“寻找” **FastAPI** 的最初想法之一。
|
||||
|
||||
/// note | 注意
|
||||
|
||||
Django REST Framework 由 Tom Christie 创建。他也是 Starlette 和 Uvicorn 的作者,**FastAPI** 就是基于它们构建的。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
提供自动化的 API 文档 Web 界面。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
|
||||
|
||||
Flask 是一个“微框架”,它不包含数据库集成,也没有像 Django 那样的许多默认内建功能。
|
||||
|
||||
这种简单与灵活使得可以将 NoSQL 数据库作为主要的数据存储系统。
|
||||
|
||||
由于非常简单,它相对直观易学,尽管文档在某些部分略显偏技术。
|
||||
|
||||
它也常用于不一定需要数据库、用户管理,或任何 Django 预构建功能的应用;当然,许多这类功能可以通过插件添加。
|
||||
|
||||
这种组件解耦、可按需扩展的“微框架”特性,是我想保留的关键点。
|
||||
|
||||
鉴于 Flask 的简洁,它似乎非常适合构建 API。接下来要找的,就是 Flask 版的 “Django REST Framework”。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
- 成为微框架,便于按需组合所需的工具与组件。
|
||||
- 提供简单易用的路由系统。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
|
||||
|
||||
**FastAPI** 实际上不是 **Requests** 的替代品。它们的作用范围完全不同。
|
||||
|
||||
在 FastAPI 应用程序内部使用 Requests 其实非常常见。
|
||||
|
||||
尽管如此,FastAPI 依然从 Requests 中获得了不少灵感。
|
||||
|
||||
**Requests** 是一个用于与 API 交互(作为客户端)的库,而 **FastAPI** 是一个用于构建 API(作为服务端)的库。
|
||||
|
||||
它们处在某种意义上的“对立端”,彼此互补。
|
||||
|
||||
Requests 设计非常简单直观,易于使用,且有合理的默认值。同时它也非常强大、可定制。
|
||||
|
||||
这就是为什么,正如其官网所说:
|
||||
|
||||
> Requests 是有史以来下载量最高的 Python 包之一
|
||||
|
||||
它的用法非常简单。例如,进行一次 `GET` 请求,你会这样写:
|
||||
|
||||
```Python
|
||||
response = requests.get("http://example.com/some/url")
|
||||
```
|
||||
|
||||
对应地,FastAPI 的 API 路径操作可能看起来是这样的:
|
||||
|
||||
```Python hl_lines="1"
|
||||
@app.get("/some/url")
|
||||
def read_url():
|
||||
return {"message": "Hello World"}
|
||||
```
|
||||
|
||||
可以看到 `requests.get(...)` 与 `@app.get(...)` 的相似之处。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
- 提供简单直观的 API。
|
||||
- 直接、自然地使用 HTTP 方法名(操作)。
|
||||
- 具备合理默认值,同时支持强大定制能力。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
|
||||
|
||||
我想从 Django REST Framework 得到的主要特性之一是自动 API 文档。
|
||||
|
||||
随后我发现有一个用于用 JSON(或 YAML,JSON 的扩展)来描述 API 的标准,称为 Swagger。
|
||||
|
||||
并且已经有了用于 Swagger API 的 Web 用户界面。因此,只要能为 API 生成 Swagger 文档,就能自动使用这个 Web 界面。
|
||||
|
||||
后来,Swagger 交由 Linux 基金会管理,并更名为 OpenAPI。
|
||||
|
||||
因此,在谈到 2.0 版本时人们常说 “Swagger”,而 3+ 版本则称为 “OpenAPI”。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
采用并使用开放的 API 规范标准,而非自定义模式。
|
||||
|
||||
并集成基于标准的用户界面工具:
|
||||
|
||||
- <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>
|
||||
- <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>
|
||||
|
||||
选择这两者是因为它们相当流行且稳定;但稍作搜索,你就能找到数十种 OpenAPI 的替代用户界面(都可以与 **FastAPI** 搭配使用)。
|
||||
|
||||
///
|
||||
|
||||
### Flask REST 框架 { #flask-rest-frameworks }
|
||||
|
||||
有若干基于 Flask 的 REST 框架,但在投入时间精力深入调研后,我发现许多已停止维护或被弃用,并存在多处未解决问题,不太适合采用。
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
API 系统所需的主要特性之一是数据“<dfn title="也称为:编组、转换">序列化</dfn>”,即将代码(Python)中的数据转换为可通过网络发送的形式。例如,将包含数据库数据的对象转换为 JSON 对象、将 `datetime` 对象转换为字符串等。
|
||||
|
||||
API 的另一个重要特性是数据校验,确保数据在给定约束下是有效的。例如,某个字段必须是 `int` 而不是任意字符串。这对传入数据尤其有用。
|
||||
|
||||
没有数据校验系统的话,你就得在代码里手写所有检查。
|
||||
|
||||
这些正是 Marshmallow 要提供的功能。它是个很棒的库,我之前大量使用过。
|
||||
|
||||
但它诞生于 Python 类型提示出现之前。因此,定义每个<dfn title="数据应如何构造的定义">模式</dfn>都需要使用 Marshmallow 提供的特定工具和类。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
使用代码定义“模式”,自动提供数据类型与校验。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
API 的另一个重要需求是从传入请求中<dfn title="读取并转换为 Python 数据">解析</dfn>数据。
|
||||
|
||||
Webargs 是一个在多个框架(包括 Flask)之上提供该功能的工具。
|
||||
|
||||
它在底层使用 Marshmallow 进行数据校验,并且由相同的开发者创建。
|
||||
|
||||
在拥有 **FastAPI** 之前,我也大量使用过它,这是个很棒的工具。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
Webargs 由与 Marshmallow 相同的开发者创建。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
对传入请求数据进行自动校验。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
|
||||
|
||||
Marshmallow 与 Webargs 通过插件提供了校验、解析与序列化。
|
||||
|
||||
但文档仍然缺失,于是出现了 APISpec。
|
||||
|
||||
它为许多框架提供插件(Starlette 也有插件)。
|
||||
|
||||
它的工作方式是:你在处理路由的每个函数的文档字符串里,用 YAML 格式编写模式定义。
|
||||
|
||||
然后它会生成 OpenAPI 模式。
|
||||
|
||||
这正是它在 Flask、Starlette、Responder 等框架里的工作方式。
|
||||
|
||||
但这样我们又回到了在 Python 字符串中维护一套“微语法”(一大段 YAML)的问题上。
|
||||
|
||||
编辑器很难为此提供帮助;而且如果我们修改了参数或 Marshmallow 模式,却忘了同步更新那个 YAML 文档字符串,生成的模式就会过时。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
APISpec 由与 Marshmallow 相同的开发者创建。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
支持开放的 API 标准 OpenAPI。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
|
||||
|
||||
这是一个 Flask 插件,将 Webargs、Marshmallow 与 APISpec 结合在一起。
|
||||
|
||||
它利用 Webargs 与 Marshmallow 的信息,通过 APISpec 自动生成 OpenAPI 模式。
|
||||
|
||||
这是个很棒却被低估的工具;它理应比许多 Flask 插件更流行。或许是因为它的文档过于简洁与抽象。
|
||||
|
||||
这解决了在 Python 文档字符串里书写 YAML(另一套语法)的问题。
|
||||
|
||||
在构建 **FastAPI** 之前,Flask + Flask-apispec + Marshmallow + Webargs 的组合是我最喜欢的后端技术栈。
|
||||
|
||||
使用它促成了若干 Flask 全栈脚手架的诞生。以下是我(以及若干外部团队)至今使用的主要技术栈:
|
||||
|
||||
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
|
||||
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
|
||||
|
||||
这些全栈脚手架也成为了[**FastAPI** 项目脚手架](project-generation.md){.internal-link target=_blank}的基础。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
Flask-apispec 由与 Marshmallow 相同的开发者创建。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
从定义序列化与校验的同一份代码自动生成 OpenAPI 模式。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a>(以及 <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
|
||||
|
||||
这甚至不是 Python。NestJS 是一个 JavaScript(TypeScript)的 NodeJS 框架,受 Angular 启发。
|
||||
|
||||
它实现了与 Flask-apispec 有些类似的效果。
|
||||
|
||||
它集成了受 Angular 2 启发的依赖注入系统。与我所知的其他依赖注入系统一样,需要预先注册“可注入项”,因此会增加冗长与重复。
|
||||
|
||||
由于参数用 TypeScript 类型描述(类似 Python 类型提示),编辑器支持相当好。
|
||||
|
||||
但由于 TypeScript 的类型在编译为 JavaScript 后不会保留,无法只依赖这些类型同时定义校验、序列化与文档。受此以及一些设计决策影响,为了获得校验、序列化与自动 schema 生成,需要在许多位置添加装饰器,因此代码会相当冗长。
|
||||
|
||||
它对嵌套模型的支持并不好。如果请求的 JSON 体是包含嵌套 JSON 对象的 JSON 对象,则无法被正确文档化和校验。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
使用 Python 类型以获得出色的编辑器支持。
|
||||
|
||||
拥有强大的依赖注入系统,并设法尽量减少代码重复。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
|
||||
|
||||
它是最早的一批基于 `asyncio` 的极速 Python 框架之一,且做得与 Flask 很相似。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
它使用了 <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> 来替代 Python 默认的 `asyncio` 循环。这正是它如此之快的原因。
|
||||
|
||||
它显然启发了 Uvicorn 和 Starlette;在公开的基准测试中,它们目前比 Sanic 更快。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
找到实现疯狂性能的路径。
|
||||
|
||||
这就是 **FastAPI** 基于 Starlette 的原因,因为它是目前可用的最快框架(由第三方基准测试验证)。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
|
||||
|
||||
Falcon 是另一个高性能 Python 框架,它被设计为精简且可作为 Hug 等其他框架的基础。
|
||||
|
||||
它设计为接收两个参数的函数:一个“request”和一个“response”。然后从 request 中“读取”,向 response 中“写入”。由于这种设计,无法用标准的 Python 类型提示将请求参数和请求体声明为函数形参。
|
||||
|
||||
因此,数据校验、序列化与文档要么需要手写完成,无法自动化;要么需要在 Falcon 之上实现一个框架,例如 Hug。其他受 Falcon 设计启发、采用“一个 request 对象 + 一个 response 对象作为参数”的框架也有同样的区别。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
寻找获得卓越性能的方法。
|
||||
|
||||
与 Hug(Hug 基于 Falcon)一起,启发 **FastAPI** 在函数中声明一个 `response` 参数。尽管在 FastAPI 中它是可选的,主要用于设置 headers、cookies 和可选的状态码。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
|
||||
|
||||
我在构建 **FastAPI** 的早期阶段发现了 Molten。它有不少相似的想法:
|
||||
|
||||
* 基于 Python 类型提示。
|
||||
* 从这些类型获得校验与文档。
|
||||
* 依赖注入系统。
|
||||
|
||||
它没有使用像 Pydantic 这样的第三方数据校验、序列化与文档库,而是有自己的实现。因此这些数据类型定义不太容易在其他地方复用。
|
||||
|
||||
它需要稍微冗长一些的配置。并且由于基于 WSGI(而非 ASGI),它并未设计为充分利用 Uvicorn、Starlette、Sanic 等工具所提供的高性能。
|
||||
|
||||
其依赖注入系统需要预先注册依赖,且依赖根据声明的类型来解析。因此无法为同一类型声明多于一个“组件”。
|
||||
|
||||
路由在一个地方集中声明,使用在其他地方声明的函数(而不是使用可以直接放在处理端点函数之上的装饰器)。这更接近 Django 的做法,而不是 Flask(和 Starlette)。它在代码中割裂了相对紧耦合的内容。
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
通过模型属性的“默认值”为数据类型定义额外校验。这提升了编辑器支持,而这在当时的 Pydantic 中尚不可用。
|
||||
|
||||
这实际上促成了对 Pydantic 的部分更新,以支持这种校验声明风格(这些功能现已在 Pydantic 中可用)。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
|
||||
|
||||
Hug 是最早使用 Python 类型提示来声明 API 参数类型的框架之一。这一绝妙想法也启发了其他工具。
|
||||
|
||||
它在声明中使用自定义类型而不是标准的 Python 类型,但这依然是巨大的进步。
|
||||
|
||||
它也是最早生成一个自定义 JSON 模式来声明整个 API 的框架之一。
|
||||
|
||||
它并不基于 OpenAPI 与 JSON Schema 这类标准。因此与其他工具(如 Swagger UI)的集成并非一帆风顺。但它仍是非常有创新性的想法。
|
||||
|
||||
它有一个有趣且少见的特性:使用同一框架,可以同时创建 API 与 CLI。
|
||||
|
||||
由于基于同步 Python Web 框架的上一代标准(WSGI),它无法处理 WebSocket 等,尽管它的性能仍然很高。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
Hug 由 Timothy Crosley 创建,他也是 <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a> 的作者,这是一个能自动排序 Python 文件中导入的优秀工具。
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI** 的想法:
|
||||
|
||||
Hug 启发了 APIStar 的部分设计,也是我当时最看好的工具之一,与 APIStar 并列。
|
||||
|
||||
Hug 促使 **FastAPI** 使用 Python 类型提示来声明参数,并自动生成定义整个 API 的模式。
|
||||
|
||||
Hug 启发 **FastAPI** 在函数中声明 `response` 参数,用于设置 headers 与 cookies。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
|
||||
|
||||
就在决定动手构建 **FastAPI** 之前,我找到了 **APIStar** 服务器。它几乎具备我想要的一切,设计也很出色。
|
||||
|
||||
在我见过的框架中,它是最早使用 Python 类型提示来声明参数和请求的实现之一(早于 NestJS 与 Molten)。我与 Hug 几乎同时发现了它。但 APIStar 使用了 OpenAPI 标准。
|
||||
|
||||
它基于相同的类型提示,在多处自动进行数据校验、序列化并生成 OpenAPI 模式。
|
||||
|
||||
请求体模式定义并未使用与 Pydantic 相同的 Python 类型提示,它更接近 Marshmallow,因此编辑器支持不如 Pydantic 好,但即便如此,APIStar 仍是当时可用的最佳选择。
|
||||
|
||||
它在当时拥有最好的性能基准(仅被 Starlette 超越)。
|
||||
|
||||
起初它没有自动 API 文档 Web 界面,但我知道我可以把 Swagger UI 加进去。
|
||||
|
||||
它有一个依赖注入系统。与上文提到的其他工具一样,需要预先注册组件。但这依然是很棒的特性。
|
||||
|
||||
我从未在完整项目中使用过它,因为它没有安全集成,因此我无法用它替代基于 Flask-apispec 的全栈脚手架所具备的全部功能。我曾把“提交一个增加该功能的 PR”放在了待办里。
|
||||
|
||||
但随后,项目的重心发生了变化。
|
||||
|
||||
它不再是一个 API Web 框架,因为作者需要专注于 Starlette。
|
||||
|
||||
现在 APIStar 是一组用于校验 OpenAPI 规范的工具,而不是 Web 框架。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
APIStar 由 Tom Christie 创建。他还创建了:
|
||||
|
||||
* Django REST Framework
|
||||
* Starlette(**FastAPI** 基于其之上)
|
||||
* Uvicorn(被 Starlette 与 **FastAPI** 使用)
|
||||
|
||||
///
|
||||
|
||||
/// check | 启发 **FastAPI**:
|
||||
|
||||
诞生。
|
||||
|
||||
用同一套 Python 类型同时声明多件事(数据校验、序列化与文档),并且还能提供出色的编辑器支持——我认为这是个极其巧妙的想法。
|
||||
|
||||
在长时间寻找与测试多种替代之后,APIStar 是当时最好的选择。
|
||||
|
||||
随后 APIStar 不再作为服务器存在,而 Starlette 出现,成为实现该体系的更佳基础。这成为构建 **FastAPI** 的最终灵感来源。
|
||||
|
||||
我把 **FastAPI** 视为 APIStar 的“精神续作”,并在此基础上,结合前述工具的经验,改进并增强了功能、类型系统及其他各方面。
|
||||
|
||||
///
|
||||
|
||||
## **FastAPI** 所使用的组件 { #used-by-fastapi }
|
||||
|
||||
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
|
||||
|
||||
Pydantic 是一个基于 Python 类型提示来定义数据校验、序列化与文档(使用 JSON Schema)的库。
|
||||
|
||||
这使得它极其直观。
|
||||
|
||||
它可与 Marshmallow 类比。尽管在基准测试中它比 Marshmallow 更快。并且由于同样基于 Python 类型提示,编辑器支持优秀。
|
||||
|
||||
/// check | **FastAPI** 用它来:
|
||||
|
||||
处理所有数据校验、数据序列化与自动模型文档(基于 JSON Schema)。
|
||||
|
||||
随后 **FastAPI** 会把这些 JSON Schema 数据纳入 OpenAPI(以及完成其他所有工作)。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette 是一个轻量级的 <dfn title="构建异步 Python Web 应用的新标准">ASGI</dfn> 框架/工具集,非常适合构建高性能的 asyncio 服务。
|
||||
|
||||
它非常简单直观。被设计为易于扩展,且具有模块化组件。
|
||||
|
||||
它具备:
|
||||
|
||||
* 性能极其出色。
|
||||
* 支持 WebSocket。
|
||||
* 进程内后台任务。
|
||||
* 启动与停止事件。
|
||||
* 基于 HTTPX 的测试客户端。
|
||||
* CORS、GZip、静态文件、流式响应。
|
||||
* 会话与 Cookie 支持。
|
||||
* 100% 测试覆盖率。
|
||||
* 100% 类型注解的代码库。
|
||||
* 极少的强依赖。
|
||||
|
||||
Starlette 目前是测试中最快的 Python 框架。仅次于 Uvicorn,它不是框架,而是服务器。
|
||||
|
||||
Starlette 提供了 Web 微框架的全部基础能力。
|
||||
|
||||
但它不提供自动的数据校验、序列化或文档。
|
||||
|
||||
这正是 **FastAPI** 在其之上增加的主要内容之一,全部基于 Python 类型提示(通过 Pydantic)。此外还有依赖注入系统、安全工具、OpenAPI 模式生成等。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
ASGI 是由 Django 核心团队成员推动的新“标准”。它尚不是正式的“Python 标准”(PEP),尽管正朝此方向推进。
|
||||
|
||||
尽管如此,已有多种工具将其作为“标准”使用。这极大提升了互操作性:你可以把 Uvicorn 换成其他 ASGI 服务器(如 Daphne 或 Hypercorn),或添加 ASGI 兼容的工具,如 `python-socketio`。
|
||||
|
||||
///
|
||||
|
||||
/// check | **FastAPI** 用它来:
|
||||
|
||||
处理所有核心 Web 部分,并在其之上扩展功能。
|
||||
|
||||
`FastAPI` 类本身直接继承自 `Starlette`。
|
||||
|
||||
因此,凡是你能用 Starlette 完成的事,也能直接用 **FastAPI** 完成;可以把它看作“加速版”的 Starlette。
|
||||
|
||||
///
|
||||
|
||||
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
|
||||
|
||||
Uvicorn 是一个基于 uvloop 与 httptools 构建的极速 ASGI 服务器。
|
||||
|
||||
它不是 Web 框架,而是服务器。例如它不提供按路径路由的工具——这是 Starlette(或 **FastAPI**)这类框架在其之上提供的功能。
|
||||
|
||||
它是 Starlette 与 **FastAPI** 推荐的服务器。
|
||||
|
||||
/// check | **FastAPI** 推荐将其作为:
|
||||
|
||||
运行 **FastAPI** 应用的主要 Web 服务器。
|
||||
|
||||
你也可以使用 `--workers` 命令行选项以获得异步的多进程服务器。
|
||||
|
||||
更多细节见[部署](deployment/index.md){.internal-link target=_blank}一节。
|
||||
|
||||
///
|
||||
|
||||
## 基准与速度 { #benchmarks-and-speed }
|
||||
|
||||
要理解、比较并查看 Uvicorn、Starlette 与 FastAPI 之间的差异,请查看[基准](benchmarks.md){.internal-link target=_blank}一节。
|
||||
|
|
@ -298,7 +298,7 @@ CPU 密集型操作的常见示例是需要复杂的数学处理。
|
|||
|
||||
这一点,再加上 Python 是**数据科学**、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 **FastAPI** 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。
|
||||
|
||||
了解如何在生产环境中实现这种并行性,可查看此文 [Deployment](deployment/index.md){.internal-link target=_blank}。
|
||||
了解如何在生产环境中实现这种并行性,可查看此文 [部署](deployment/index.md){.internal-link target=_blank}。
|
||||
|
||||
## `async` 和 `await` { #async-and-await }
|
||||
|
||||
|
|
@ -369,7 +369,7 @@ Starlette (和 **FastAPI**) 是基于 <a href="https://anyio.readthedocs.io/
|
|||
|
||||
即使你没有使用 **FastAPI**,你也可以使用 <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。
|
||||
|
||||
我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的**自动补全**、**内联错误提示**等功能。这个库还附带了一个友好的入门指南和教程,能帮助你**理解**并编写**自己的异步代码**:<a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>。如果你有**结合使用异步代码和常规**(阻塞/同步)代码的需求,这个库会特别有用。
|
||||
我基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的**自动补全**、**内联错误提示**等功能。这个库还附带了一个友好的入门指南和教程,能帮助你**理解**并编写**自己的异步代码**:<a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>。如果你有**结合使用异步代码和常规**(阻塞/同步)代码的需求,这个库会特别有用。
|
||||
|
||||
### 其他形式的异步代码 { #other-forms-of-asynchronous-code }
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
* 你不会直接在 Uvicorn 中编写应用程序。这意味着你的代码至少必须包含 Starlette(或 **FastAPI**)提供的代码。如果你这样做了(即直接在 Uvicorn 中编写应用程序),最终的应用程序会和使用了框架并且最小化了应用代码和 bug 的情况具有相同的性能损耗。
|
||||
* 如果你要对比 Uvicorn,请将其与 Daphne,Hypercorn,uWSGI 等应用服务器进行比较。
|
||||
* **Starlette**:
|
||||
* 在 Uvicorn 后使用 Starlette,性能会略有下降。实际上,Starlette 使用 Uvicorn 运行。因此,由于必须执行更多的代码,它只会比 Uvicorn 更慢。
|
||||
* 性能仅次于 Uvicorn。实际上,Starlette 使用 Uvicorn 运行。因此,由于必须执行更多的代码,它只会比 Uvicorn 更慢。
|
||||
* 但它为你提供了构建简单的网络程序的工具,并具有基于路径的路由等功能。
|
||||
* 如果想对比与 Starlette 对标的开发框架,请将其与 Sanic,Flask,Django 等网络框架(或微框架)进行比较。
|
||||
* **FastAPI**:
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@
|
|||
|
||||
### 工作进程和端口 { #worker-processes-and-ports }
|
||||
|
||||
还记得文档 [About HTTPS](https.md){.internal-link target=_blank} 中只有一个进程可以侦听服务器中的端口和 IP 地址的一种组合吗?
|
||||
还记得文档 [关于 HTTPS](https.md){.internal-link target=_blank} 中只有一个进程可以侦听服务器中的端口和 IP 地址的一种组合吗?
|
||||
|
||||
现在仍然是对的。
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<summary>Dockerfile 预览 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ def read_item(item_id: int, q: str | None = None):
|
|||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
|
|
@ -390,7 +390,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
|||
然后你只需要在 `Dockerfile` 中修改相应路径来复制该文件:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ Traefik 与 Docker、Kubernetes 等都有集成,因此为容器设置和配置
|
|||
|
||||
## 复制 - 进程数 { #replication-number-of-processes }
|
||||
|
||||
如果你有一个由 **Kubernetes**、Docker Swarm Mode、Nomad 或其他类似的复杂系统管理的、在多台机器上运行的分布式容器<abbr title="被配置为以某种方式连接并协同工作的多台机器">集群</abbr>,那么你很可能会希望在**集群层面**来**处理复制**,而不是在每个容器中使用**进程管理**(比如让 Uvicorn 运行多个 workers)。
|
||||
如果你有一个由 **Kubernetes**、Docker Swarm Mode、Nomad 或其他类似的复杂系统管理的、在多台机器上运行的分布式容器<dfn title="被配置为以某种方式连接并协同工作的多台机器">集群</dfn>,那么你很可能会希望在**集群层面**来**处理复制**,而不是在每个容器中使用**进程管理**(比如让 Uvicorn 运行多个 workers)。
|
||||
|
||||
像 Kubernetes 这样的分布式容器管理系统通常都有某种内置方式来处理**容器复制**,同时对传入请求进行**负载均衡**。这一切都在**集群层面**完成。
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ Traefik 与 Docker、Kubernetes 等都有集成,因此为容器设置和配置
|
|||
在这些情况下,你可以使用 `--workers` 命令行选项来设置要运行的 worker 数量:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# FastAPI Cloud { #fastapi-cloud }
|
||||
|
||||
你可以用**一条命令**将你的 FastAPI 应用部署到 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>,如果还没有,去加入候补名单吧。🚀
|
||||
|
||||
## 登录 { #login }
|
||||
|
||||
请确保你已有 **FastAPI Cloud** 账号(我们已从候补名单向你发出邀请 😉)。
|
||||
|
||||
然后登录:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## 部署 { #deploy }
|
||||
|
||||
现在用**一条命令**部署你的应用:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi deploy
|
||||
|
||||
Deploying to FastAPI Cloud...
|
||||
|
||||
✅ Deployment successful!
|
||||
|
||||
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
就这样!现在你可以通过该 URL 访问你的应用。✨
|
||||
|
||||
## 关于 FastAPI Cloud { #about-fastapi-cloud }
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** 由 **FastAPI** 背后的作者与团队打造。
|
||||
|
||||
它让你以最小的投入完成 API 的**构建**、**部署**与**访问**。
|
||||
|
||||
它把使用 FastAPI 构建应用时的**开发者体验**,同样带到将应用**部署**到云上的过程。🎉
|
||||
|
||||
它还会替你处理部署应用时大多数需要的事项,例如:
|
||||
|
||||
* HTTPS
|
||||
* 副本、基于请求的自动伸缩
|
||||
* 等等
|
||||
|
||||
FastAPI Cloud 是 *FastAPI and friends* 开源项目的主要赞助方与资金提供者。✨
|
||||
|
||||
## 部署到其他云服务商 { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI 是开源的,并基于标准。你可以将 FastAPI 应用部署到你选择的任意云服务商。
|
||||
|
||||
按照你所选云服务商的指南部署 FastAPI 应用即可。🤓
|
||||
|
||||
## 部署到你自己的服务器 { #deploy-your-own-server }
|
||||
|
||||
在后面的**部署**指南中,我也会讲解所有细节,帮助你理解幕后发生了什么、需要做什么,以及如何自行部署 FastAPI 应用,包括部署到你自己的服务器。🤓
|
||||
|
|
@ -65,13 +65,13 @@
|
|||
|
||||
第一步我们要先**获取**一些**域名(Domain Name)**。 然后可以在 DNS 服务器(可能是你的同一家云服务商提供的)中配置它。
|
||||
|
||||
你可能拥有一个云服务器(虚拟机)或类似的东西,并且它会有一个<abbr title="不会改变">固定</abbr> **公共IP地址**。
|
||||
你可能拥有一个云服务器(虚拟机)或类似的东西,并且它会有一个<dfn title="不会随时间改变。非动态的。">固定</dfn> **公共IP地址**。
|
||||
|
||||
在 DNS 服务器中,你可以配置一条记录(“A 记录”)以将 **你的域名** 指向你服务器的公共 **IP 地址**。
|
||||
|
||||
这个操作一般只需要在最开始执行一次。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
域名这部分发生在 HTTPS 之前,由于这一切都依赖于域名和 IP 地址,所以先在这里提一下。
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ TLS 终止代理可以访问一个或多个 **TLS 证书**(HTTPS 证书)。
|
|||
|
||||
这就是 **HTTPS**,它只是 **安全 TLS 连接** 内的普通 **HTTP**,而不是纯粹的(未加密的)TCP 连接。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
请注意,通信加密发生在 **TCP 层**,而不是 HTTP 层。
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ TLS 终止代理将使用协商好的加密算法**解密请求**,并将**(
|
|||
|
||||
这在需要正确处理重定向等场景时很有用。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
你可以在文档中了解更多:[在代理之后 - 启用代理转发请求头](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ $ pip install "uvicorn[standard]"
|
|||
|
||||
类似的流程也适用于任何其他 ASGI 服务器程序。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
通过添加 `standard` 选项,Uvicorn 将安装并使用一些推荐的额外依赖项。
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ $ uvicorn main:app --host 0.0.0.0 --port 80
|
|||
|
||||
</div>
|
||||
|
||||
/// note
|
||||
/// note | 注意
|
||||
|
||||
命令 `uvicorn main:app` 的含义如下:
|
||||
|
||||
|
|
@ -131,7 +131,7 @@ from main import app
|
|||
|
||||
每种 ASGI 服务器程序通常都会有类似的命令,您可以在它们的官方文档中找到更多信息。
|
||||
|
||||
/// warning
|
||||
/// warning | 警告
|
||||
|
||||
Uvicorn 和其他服务器支持 `--reload` 选项,该选项在开发过程中非常有用。
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
### 基于开放标准 { #based-on-open-standards }
|
||||
|
||||
* 用于创建 API 的 <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a>,包含对<abbr title="也被叫做: endpoints, routes">路径</abbr> <abbr title="也叫做 HTTP 方法,如 POST、GET、PUT、DELETE">操作</abbr>、参数、请求体、安全等的声明。
|
||||
* 用于创建 API 的 <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a>,包含对<dfn title="也称为:端点、路由">路径</dfn> <dfn title="也称为 HTTP 方法,如 POST、GET、PUT、DELETE">操作</dfn>、参数、请求体、安全等的声明。
|
||||
* 使用 <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> 自动生成数据模型文档(因为 OpenAPI 本身就是基于 JSON Schema 的)。
|
||||
* 经过了缜密的研究后围绕这些标准而设计。并非狗尾续貂。
|
||||
* 这也允许了在很多语言中自动**生成客户端代码**。
|
||||
|
|
@ -137,7 +137,7 @@ OpenAPI 中定义的安全模式,包括:
|
|||
|
||||
### 依赖注入 { #dependency-injection }
|
||||
|
||||
FastAPI 有一个使用非常简单,但是非常强大的<abbr title='也叫做 "components", "resources", "services", "providers"'><strong>依赖注入</strong></abbr>系统。
|
||||
FastAPI 有一个使用非常简单,但是非常强大的<dfn title='也称为 "components"、"resources"、"services"、"providers"'><strong>依赖注入</strong></dfn>系统。
|
||||
|
||||
* 甚至依赖也可以有依赖,创建一个层级或者**“图”依赖**。
|
||||
* 所有**自动化处理**都由框架完成。
|
||||
|
|
@ -154,8 +154,8 @@ FastAPI 有一个使用非常简单,但是非常强大的<abbr title='也叫
|
|||
|
||||
### 测试 { #tested }
|
||||
|
||||
* 100% <abbr title="自动测试的代码量">测试覆盖</abbr>。
|
||||
* 代码库100% <abbr title="Python类型注解,有了这个你的编辑器和外部工具可以给你更好的支持">类型注释</abbr>。
|
||||
* 100% <dfn title="自动测试的代码量">测试覆盖</dfn>。
|
||||
* 代码库100% <dfn title="Python 类型注解,有了这个你的编辑器和外部工具可以给你更好的支持">类型注释</dfn>。
|
||||
* 用于生产应用。
|
||||
|
||||
## Starlette 特性 { #starlette-features }
|
||||
|
|
@ -186,10 +186,10 @@ FastAPI 有一个使用非常简单,但是非常强大的<abbr title='也叫
|
|||
|
||||
通过 **FastAPI** 你可以获得所有 **Pydantic**(FastAPI 基于 Pydantic 做了所有的数据处理):
|
||||
|
||||
* **更简单**:
|
||||
* **不烧脑**:
|
||||
* 没有新的模式定义 micro-language 需要学习。
|
||||
* 如果你知道 Python types,你就知道如何使用 Pydantic。
|
||||
* 和你 **<abbr title="集成开发环境,和代码编辑器类似">IDE</abbr>/<abbr title="一个检查代码错误的程序">linter</abbr>/brain** 适配:
|
||||
* 和你 **<abbr title="Integrated Development Environment - 集成开发环境: 类似于代码编辑器">IDE</abbr>/<dfn title="一个检查代码错误的程序">linter</dfn>/brain** 适配:
|
||||
* 因为 pydantic 数据结构仅仅是你定义的类的实例;自动补全,linting,mypy 以及你的直觉应该可以和你验证的数据一起正常工作。
|
||||
* 验证**复杂结构**:
|
||||
* 使用分层的 Pydantic 模型,Python `typing` 的 `List` 和 `Dict` 等等。
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
## 发推谈谈 **FastAPI** { #tweet-about-fastapi }
|
||||
|
||||
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Tweet about **FastAPI**</a>,告诉我和大家你为什么喜欢它。🎉
|
||||
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">发推谈谈 **FastAPI**</a>,告诉我和大家你为什么喜欢它。🎉
|
||||
|
||||
我很高兴听到 **FastAPI** 的使用情况、你喜欢它的哪些点、你在哪个项目/公司使用它,等等。
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# 使用旧的 403 认证错误状态码 { #use-old-403-authentication-error-status-codes }
|
||||
|
||||
在 FastAPI `0.122.0` 版本之前,当内置的安全工具在认证失败后向客户端返回错误时,会使用 HTTP 状态码 `403 Forbidden`。
|
||||
|
||||
从 FastAPI `0.122.0` 版本开始,它们改用更合适的 HTTP 状态码 `401 Unauthorized`,并在响应中返回合理的 `WWW-Authenticate` 头,遵循 HTTP 规范,<a href="https://datatracker.ietf.org/doc/html/rfc7235#section-3.1" class="external-link" target="_blank">RFC 7235</a>、<a href="https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized" class="external-link" target="_blank">RFC 9110</a>。
|
||||
|
||||
但如果由于某些原因你的客户端依赖旧行为,你可以在你的安全类中重写方法 `make_not_authenticated_error` 来回退到旧行为。
|
||||
|
||||
例如,你可以创建一个 `HTTPBearer` 的子类,使其返回 `403 Forbidden` 错误,而不是默认的 `401 Unauthorized` 错误:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意该函数返回的是异常实例,而不是直接抛出它。抛出操作由其余的内部代码完成。
|
||||
|
||||
///
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# 按条件配置 OpenAPI { #conditional-openapi }
|
||||
|
||||
如果需要,你可以使用设置和环境变量,按环境有条件地配置 OpenAPI,甚至完全禁用它。
|
||||
|
||||
## 关于安全、API 和文档 { #about-security-apis-and-docs }
|
||||
|
||||
在生产环境隐藏文档界面并不应该成为保护 API 的方式。
|
||||
|
||||
这并不会给你的 API 增加任何额外的安全性,*路径操作* 仍然会在原来的位置可用。
|
||||
|
||||
如果你的代码里有安全漏洞,它仍然存在。
|
||||
|
||||
隐藏文档只会让理解如何与 API 交互变得更困难,也可能让你在生产环境中调试更困难。这大体上可以被视为一种 <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">通过隐藏实现安全</a> 的做法。
|
||||
|
||||
如果你想保护你的 API,有很多更好的措施,例如:
|
||||
|
||||
- 确保为请求体和响应定义完善的 Pydantic 模型。
|
||||
- 使用依赖配置所需的权限和角色。
|
||||
- 绝不要存储明文密码,只存储密码哈希。
|
||||
- 实现并使用成熟的密码学工具,比如 pwdlib 和 JWT 令牌等。
|
||||
- 在需要的地方使用 OAuth2 作用域添加更细粒度的权限控制。
|
||||
- ...等。
|
||||
|
||||
尽管如此,你可能确实有非常特定的用例,需要在某些环境(例如生产环境)禁用 API 文档,或根据环境变量的配置来决定。
|
||||
|
||||
## 基于设置和环境变量的条件式 OpenAPI { #conditional-openapi-from-settings-and-env-vars }
|
||||
|
||||
你可以很容易地使用相同的 Pydantic 设置来配置生成的 OpenAPI 和文档 UI。
|
||||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
|
||||
|
||||
这里我们声明了设置项 `openapi_url`,其默认值同样是 `"/openapi.json"`。
|
||||
|
||||
然后在创建 `FastAPI` 应用时使用它。
|
||||
|
||||
接着,你可以通过把环境变量 `OPENAPI_URL` 设为空字符串来禁用 OpenAPI(包括文档 UI),例如:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ OPENAPI_URL= uvicorn main:app
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
然后如果你访问 `/openapi.json`、`/docs` 或 `/redoc`,就会得到一个 `404 Not Found` 错误,例如:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Not Found"
|
||||
}
|
||||
```
|
||||
|
|
@ -18,7 +18,7 @@ FastAPI会将这些配置转换为 **JSON**,使其与 JavaScript 兼容,因
|
|||
|
||||
但是你可以通过设置 `syntaxHighlight` 为 `False` 来禁用 Swagger UI 中的语法高亮:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
|
||||
|
||||
...在此之后,Swagger UI 将不会高亮代码:
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ FastAPI会将这些配置转换为 **JSON**,使其与 JavaScript 兼容,因
|
|||
|
||||
同样地,你也可以通过设置键 `"syntaxHighlight.theme"` 来设置语法高亮主题(注意中间有一个点):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
|
||||
|
||||
这个配置会改变语法高亮主题:
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ FastAPI 包含了一些默认配置参数,适用于大多数用例。
|
|||
|
||||
比如,如果要禁用 `deepLinking`,你可以像这样传递设置到 `swagger_ui_parameters` 中:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
|
||||
|
||||
## 其他 Swagger UI 参数 { #other-swagger-ui-parameters }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
# 自托管自定义文档 UI 静态资源 { #custom-docs-ui-static-assets-self-hosting }
|
||||
|
||||
API 文档使用 Swagger UI 和 ReDoc,它们各自需要一些 JavaScript 和 CSS 文件。
|
||||
|
||||
默认情况下,这些文件从一个 <abbr title="Content Delivery Network - 内容分发网络: 一种服务,通常由多台服务器组成,用于提供静态文件,如 JavaScript 和 CSS。它常用于从更接近客户端的服务器提供这些文件,从而提升性能。">CDN</abbr> 提供。
|
||||
|
||||
不过你可以自定义:可以指定特定的 CDN,或自行提供这些文件。
|
||||
|
||||
## 为 JavaScript 和 CSS 自定义 CDN { #custom-cdn-for-javascript-and-css }
|
||||
|
||||
假设你想使用不同的 <abbr title="Content Delivery Network - 内容分发网络">CDN</abbr>,例如使用 `https://unpkg.com/`。
|
||||
|
||||
如果你所在的国家/地区屏蔽了某些 URL,这会很有用。
|
||||
|
||||
### 关闭自动文档 { #disable-the-automatic-docs }
|
||||
|
||||
第一步是关闭自动文档,因为默认它们会使用默认的 CDN。
|
||||
|
||||
要关闭它们,在创建 `FastAPI` 应用时将其 URL 设为 `None`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
|
||||
|
||||
### 包含自定义文档 { #include-the-custom-docs }
|
||||
|
||||
现在你可以为自定义文档创建*路径操作*。
|
||||
|
||||
你可以复用 FastAPI 的内部函数来创建文档的 HTML 页面,并传入所需参数:
|
||||
|
||||
- `openapi_url`:文档 HTML 页面获取你的 API 的 OpenAPI 模式的 URL。这里可以使用 `app.openapi_url` 属性。
|
||||
- `title`:你的 API 标题。
|
||||
- `oauth2_redirect_url`:这里可以使用 `app.swagger_ui_oauth2_redirect_url` 来使用默认值。
|
||||
- `swagger_js_url`:你的 Swagger UI 文档 HTML 获取**JavaScript** 文件的 URL。这里是自定义的 CDN URL。
|
||||
- `swagger_css_url`:你的 Swagger UI 文档 HTML 获取**CSS** 文件的 URL。这里是自定义的 CDN URL。
|
||||
|
||||
ReDoc 也类似...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
`swagger_ui_redirect` 的*路径操作*是在你使用 OAuth2 时的一个辅助。
|
||||
|
||||
如果你把 API 与某个 OAuth2 提供方集成,你就可以完成认证并带着获取到的凭据回到 API 文档里。然后使用真实的 OAuth2 认证与之交互。
|
||||
|
||||
Swagger UI 会在幕后为你处理这些,但它需要这个“重定向”辅助路径。
|
||||
|
||||
///
|
||||
|
||||
### 创建一个路径操作进行测试 { #create-a-path-operation-to-test-it }
|
||||
|
||||
现在,为了测试一切是否正常,创建一个*路径操作*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
|
||||
|
||||
### 测试 { #test-it }
|
||||
|
||||
现在,你应该可以访问 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>,并刷新页面,页面会从新的 CDN 加载这些资源。
|
||||
|
||||
## 为文档自托管 JavaScript 和 CSS { #self-hosting-javascript-and-css-for-docs }
|
||||
|
||||
如果你需要在离线、无法访问互联网或仅在局域网内时,应用仍能工作,那么自托管 JavaScript 和 CSS 会很有用。
|
||||
|
||||
这里你将看到如何在同一个 FastAPI 应用中自行提供这些文件,并配置文档使用它们。
|
||||
|
||||
### 项目文件结构 { #project-file-structure }
|
||||
|
||||
假设你的项目文件结构如下:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
```
|
||||
|
||||
现在创建一个目录来存放这些静态文件。
|
||||
|
||||
你的新文件结构可能如下:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
└── static/
|
||||
```
|
||||
|
||||
### 下载文件 { #download-the-files }
|
||||
|
||||
下载文档需要的静态文件,并将它们放到 `static/` 目录中。
|
||||
|
||||
你通常可以右键点击每个链接,选择类似“将链接另存为...”的选项。
|
||||
|
||||
Swagger UI 使用以下文件:
|
||||
|
||||
- <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
|
||||
- <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
|
||||
|
||||
而 ReDoc 使用以下文件:
|
||||
|
||||
- <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
|
||||
|
||||
之后,你的文件结构可能如下:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
└── static
|
||||
├── redoc.standalone.js
|
||||
├── swagger-ui-bundle.js
|
||||
└── swagger-ui.css
|
||||
```
|
||||
|
||||
### 提供静态文件 { #serve-the-static-files }
|
||||
|
||||
- 导入 `StaticFiles`。
|
||||
- 在特定路径上“挂载”一个 `StaticFiles()` 实例。
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
|
||||
|
||||
### 测试静态文件 { #test-the-static-files }
|
||||
|
||||
启动你的应用,并访问 <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>。
|
||||
|
||||
你应该会看到一个非常长的 **ReDoc** 的 JavaScript 文件。
|
||||
|
||||
它可能以如下内容开头:
|
||||
|
||||
```JavaScript
|
||||
/*! For license information please see redoc.standalone.js.LICENSE.txt */
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
|
||||
...
|
||||
```
|
||||
|
||||
这就确认了你的应用能够提供静态文件,并且你把文档所需的静态文件放在了正确的位置。
|
||||
|
||||
现在我们可以配置应用,让文档使用这些静态文件。
|
||||
|
||||
### 为静态文件关闭自动文档 { #disable-the-automatic-docs-for-static-files }
|
||||
|
||||
和使用自定义 CDN 一样,第一步是关闭自动文档,因为默认情况下它们会使用 CDN。
|
||||
|
||||
要关闭它们,在创建 `FastAPI` 应用时将其 URL 设为 `None`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
|
||||
|
||||
### 为静态文件包含自定义文档 { #include-the-custom-docs-for-static-files }
|
||||
|
||||
同样地,现在你可以为自定义文档创建*路径操作*。
|
||||
|
||||
你可以再次复用 FastAPI 的内部函数来创建文档的 HTML 页面,并传入所需参数:
|
||||
|
||||
- `openapi_url`:文档 HTML 页面获取你的 API 的 OpenAPI 模式的 URL。这里可以使用 `app.openapi_url` 属性。
|
||||
- `title`:你的 API 标题。
|
||||
- `oauth2_redirect_url`:这里可以使用 `app.swagger_ui_oauth2_redirect_url` 来使用默认值。
|
||||
- `swagger_js_url`:你的 Swagger UI 文档 HTML 获取**JavaScript** 文件的 URL。**这是现在由你的应用自己提供的那个**。
|
||||
- `swagger_css_url`:你的 Swagger UI 文档 HTML 获取**CSS** 文件的 URL。**这是现在由你的应用自己提供的那个**。
|
||||
|
||||
ReDoc 也类似...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
`swagger_ui_redirect` 的*路径操作*是在你使用 OAuth2 时的一个辅助。
|
||||
|
||||
如果你把 API 与某个 OAuth2 提供方集成,你就可以完成认证并带着获取到的凭据回到 API 文档里。然后使用真实的 OAuth2 认证与之交互。
|
||||
|
||||
Swagger UI 会在幕后为你处理这些,但它需要这个“重定向”辅助路径。
|
||||
|
||||
///
|
||||
|
||||
### 创建一个路径操作测试静态文件 { #create-a-path-operation-to-test-static-files }
|
||||
|
||||
现在,为了测试一切是否正常,创建一个*路径操作*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
|
||||
|
||||
### 测试静态文件 UI { #test-static-files-ui }
|
||||
|
||||
现在,你可以断开 WiFi,访问 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>,并刷新页面。
|
||||
|
||||
即使没有互联网,你也能看到 API 的文档并与之交互。
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# 自定义 Request 和 APIRoute 类 { #custom-request-and-apiroute-class }
|
||||
|
||||
在某些情况下,你可能想要重写 `Request` 和 `APIRoute` 类使用的逻辑。
|
||||
|
||||
尤其是,当你本来会把这些逻辑放到中间件里时,这是一个不错的替代方案。
|
||||
|
||||
例如,如果你想在应用处理之前读取或操作请求体。
|
||||
|
||||
/// danger | 危险
|
||||
|
||||
这是一个“高级”特性。
|
||||
|
||||
如果你刚开始使用 **FastAPI**,可以先跳过本节。
|
||||
|
||||
///
|
||||
|
||||
## 使用场景 { #use-cases }
|
||||
|
||||
一些使用场景包括:
|
||||
|
||||
* 将非 JSON 的请求体转换为 JSON(例如 <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>)。
|
||||
* 解压缩使用 gzip 压缩的请求体。
|
||||
* 自动记录所有请求体日志。
|
||||
|
||||
## 处理自定义请求体编码 { #handling-custom-request-body-encodings }
|
||||
|
||||
来看如何用自定义的 `Request` 子类来解压 gzip 请求。
|
||||
|
||||
以及一个 `APIRoute` 子类来使用该自定义请求类。
|
||||
|
||||
### 创建自定义 `GzipRequest` 类 { #create-a-custom-gziprequest-class }
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
这是一个演示工作原理的示例。如果你需要 Gzip 支持,可以直接使用提供的 [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}。
|
||||
|
||||
///
|
||||
|
||||
首先,我们创建一个 `GzipRequest` 类,它会重写 `Request.body()` 方法:当请求头中存在相应标记时对请求体进行解压。
|
||||
|
||||
如果请求头中没有 `gzip`,则不会尝试解压。
|
||||
|
||||
这样,同一个路由类即可同时处理 gzip 压缩和未压缩的请求。
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
|
||||
|
||||
### 创建自定义 `GzipRoute` 类 { #create-a-custom-gziproute-class }
|
||||
|
||||
接着,我们创建 `fastapi.routing.APIRoute` 的自定义子类来使用 `GzipRequest`。
|
||||
|
||||
这次,我们会重写 `APIRoute.get_route_handler()` 方法。
|
||||
|
||||
该方法返回一个函数,这个函数负责接收请求并返回响应。
|
||||
|
||||
这里我们用它把原始请求包装为 `GzipRequest`。
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
`Request` 拥有 `request.scope` 属性,它就是一个 Python `dict`,包含与请求相关的元数据。
|
||||
|
||||
`Request` 还包含 `request.receive`,它是一个用于“接收”请求体的函数。
|
||||
|
||||
`scope` 字典和 `receive` 函数都是 ASGI 规范的一部分。
|
||||
|
||||
创建一个新的 `Request` 实例需要这两样:`scope` 和 `receive`。
|
||||
|
||||
想了解更多关于 `Request` 的信息,请查看 <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">Starlette 的 Request 文档</a>。
|
||||
|
||||
///
|
||||
|
||||
由 `GzipRequest.get_route_handler` 返回的函数唯一不同之处是把 `Request` 转换为 `GzipRequest`。
|
||||
|
||||
这样,在传给我们的路径操作之前,`GzipRequest` 会(在需要时)负责解压数据。
|
||||
|
||||
之后,其余处理逻辑完全相同。
|
||||
|
||||
但由于我们修改了 `GzipRequest.body`,在 **FastAPI** 需要读取时,请求体会被自动解压。
|
||||
|
||||
## 在异常处理器中访问请求体 { #accessing-the-request-body-in-an-exception-handler }
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
要解决类似问题,使用 `RequestValidationError` 的自定义处理器中的 `body` 往往更简单([处理错误](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank})。
|
||||
|
||||
但本示例同样有效,并展示了如何与内部组件交互。
|
||||
|
||||
///
|
||||
|
||||
我们也可以用相同的方法在异常处理器中访问请求体。
|
||||
|
||||
所需仅是在 `try`/`except` 块中处理请求:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
|
||||
|
||||
如果发生异常,`Request` 实例仍在作用域内,因此我们可以在处理错误时读取并使用请求体:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
|
||||
|
||||
## 在路由器中自定义 `APIRoute` 类 { #custom-apiroute-class-in-a-router }
|
||||
|
||||
你也可以设置 `APIRouter` 的 `route_class` 参数:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
|
||||
|
||||
在此示例中,`router` 下的路径操作将使用自定义的 `TimedRoute` 类,响应中会多一个 `X-Response-Time` 头,包含生成响应所用的时间:
|
||||
|
||||
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# 扩展 OpenAPI { #extending-openapi }
|
||||
|
||||
在某些情况下,你可能需要修改生成的 OpenAPI 架构(schema)。
|
||||
|
||||
本节将介绍如何实现。
|
||||
|
||||
## 常规流程 { #the-normal-process }
|
||||
|
||||
常规(默认)流程如下。
|
||||
|
||||
`FastAPI` 应用(实例)有一个 `.openapi()` 方法,预期返回 OpenAPI 架构。
|
||||
|
||||
在创建应用对象时,会注册一个用于 `/openapi.json`(或你在 `openapi_url` 中设置的路径)的路径操作。
|
||||
|
||||
它只会返回一个 JSON 响应,内容是应用 `.openapi()` 方法的结果。
|
||||
|
||||
默认情况下,`.openapi()` 方法会检查属性 `.openapi_schema` 是否已有内容,若有则直接返回。
|
||||
|
||||
如果没有,则使用 `fastapi.openapi.utils.get_openapi` 工具函数生成。
|
||||
|
||||
该 `get_openapi()` 函数接收以下参数:
|
||||
|
||||
- `title`:OpenAPI 标题,显示在文档中。
|
||||
- `version`:你的 API 版本,例如 `2.5.0`。
|
||||
- `openapi_version`:使用的 OpenAPI 规范版本。默认是最新的 `3.1.0`。
|
||||
- `summary`:API 的简短摘要。
|
||||
- `description`:API 的描述,可包含 Markdown,并会展示在文档中。
|
||||
- `routes`:路由列表,即已注册的每个路径操作。来自 `app.routes`。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
参数 `summary` 仅在 OpenAPI 3.1.0 及更高版本中可用,FastAPI 0.99.0 及以上版本支持。
|
||||
|
||||
///
|
||||
|
||||
## 覆盖默认值 { #overriding-the-defaults }
|
||||
|
||||
基于以上信息,你可以用同一个工具函数生成 OpenAPI 架构,并按需覆盖其中的各个部分。
|
||||
|
||||
例如,让我们添加 <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">ReDoc 的 OpenAPI 扩展以包含自定义 Logo</a>。
|
||||
|
||||
### 常规 **FastAPI** { #normal-fastapi }
|
||||
|
||||
首先,像平常一样编写你的 **FastAPI** 应用:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
|
||||
|
||||
### 生成 OpenAPI 架构 { #generate-the-openapi-schema }
|
||||
|
||||
然后,在一个 `custom_openapi()` 函数中使用同一个工具函数生成 OpenAPI 架构:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
|
||||
|
||||
### 修改 OpenAPI 架构 { #modify-the-openapi-schema }
|
||||
|
||||
现在你可以添加 ReDoc 扩展,在 OpenAPI 架构的 `info` “对象”中加入自定义 `x-logo`:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
|
||||
|
||||
### 缓存 OpenAPI 架构 { #cache-the-openapi-schema }
|
||||
|
||||
你可以把 `.openapi_schema` 属性当作“缓存”,用来存储已生成的架构。
|
||||
|
||||
这样一来,用户每次打开 API 文档时,应用就不必重新生成架构。
|
||||
|
||||
它只会生成一次,后续请求都会使用同一份缓存的架构。
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
|
||||
|
||||
### 覆盖方法 { #override-the-method }
|
||||
|
||||
现在你可以用你的新函数替换 `.openapi()` 方法。
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
|
||||
|
||||
### 验证 { #check-it }
|
||||
|
||||
当你访问 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> 时,你会看到已使用你的自定义 Logo(本例中为 **FastAPI** 的 Logo):
|
||||
|
||||
<img src="/img/tutorial/extending-openapi/image01.png">
|
||||
|
|
@ -36,4 +36,4 @@
|
|||
|
||||
## OpenAPI 文档 URL { #openapi-docs-urls }
|
||||
|
||||
要更改用于自动生成文档的 URL,请阅读 [教程 - 元数据和文档 URL](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}。
|
||||
要更改自动生成的文档用户界面所使用的 URL,请阅读 [教程 - 元数据和文档 URL](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# GraphQL { #graphql }
|
||||
|
||||
由于 **FastAPI** 基于 **ASGI** 标准,因此很容易集成任何也兼容 ASGI 的 **GraphQL** 库。
|
||||
|
||||
你可以在同一个应用中将常规的 FastAPI 路径操作与 GraphQL 结合使用。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
**GraphQL** 解决一些非常特定的用例。
|
||||
|
||||
与常见的 **Web API** 相比,它有各自的**优点**和**缺点**。
|
||||
|
||||
请确保评估在你的用例中,这些**好处**是否足以弥补这些**缺点**。 🤓
|
||||
|
||||
///
|
||||
|
||||
## GraphQL 库 { #graphql-libraries }
|
||||
|
||||
以下是一些支持 **ASGI** 的 **GraphQL** 库。你可以将它们与 **FastAPI** 一起使用:
|
||||
|
||||
* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> 🍓
|
||||
* 提供 <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">面向 FastAPI 的文档</a>
|
||||
* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
|
||||
* 提供 <a href="https://ariadnegraphql.org/docs/fastapi-integration" class="external-link" target="_blank">面向 FastAPI 的文档</a>
|
||||
* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
|
||||
* 提供用于 ASGI 集成的 <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a>
|
||||
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
|
||||
* 可配合 <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a> 使用
|
||||
|
||||
## 使用 Strawberry 的 GraphQL { #graphql-with-strawberry }
|
||||
|
||||
如果你需要或想要使用 **GraphQL**,<a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> 是**推荐**的库,因为它的设计与 **FastAPI** 最为接近,全部基于**类型注解**。
|
||||
|
||||
根据你的用例,你可能会更喜欢其他库,但如果你问我,我大概率会建议你先试试 **Strawberry**。
|
||||
|
||||
下面是一个将 Strawberry 与 FastAPI 集成的小预览:
|
||||
|
||||
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
|
||||
|
||||
你可以在 <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry 文档</a>中了解更多信息。
|
||||
|
||||
还有关于 <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">将 Strawberry 与 FastAPI 结合使用</a>的文档。
|
||||
|
||||
## Starlette 中较早的 `GraphQLApp` { #older-graphqlapp-from-starlette }
|
||||
|
||||
早期版本的 Starlette 包含一个 `GraphQLApp` 类,用于与 <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a> 集成。
|
||||
|
||||
它已在 Starlette 中被弃用,但如果你的代码使用了它,你可以轻松**迁移**到 <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>,它覆盖相同的用例,且接口**几乎完全一致**。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
如果你需要 GraphQL,我仍然建议看看 <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>,因为它基于类型注解而不是自定义类和类型。
|
||||
|
||||
///
|
||||
|
||||
## 了解更多 { #learn-more }
|
||||
|
||||
你可以在 <a href="https://graphql.org/" class="external-link" target="_blank">GraphQL 官方文档</a>中了解更多关于 **GraphQL** 的内容。
|
||||
|
||||
你也可以通过上面的链接阅读各个库的更多信息。
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
# 从 Pydantic v1 迁移到 Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
|
||||
|
||||
如果你有一个较旧的 FastAPI 应用,可能在使用 Pydantic v1。
|
||||
|
||||
FastAPI 0.100.0 同时支持 Pydantic v1 和 v2,会使用你已安装的任一版本。
|
||||
|
||||
FastAPI 0.119.0 引入了在 Pydantic v2 内部以 `pydantic.v1` 形式对 Pydantic v1 的部分支持,以便于迁移到 v2。
|
||||
|
||||
FastAPI 0.126.0 移除了对 Pydantic v1 的支持,但在一段时间内仍支持 `pydantic.v1`。
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
从 Python 3.14 开始,Pydantic 团队不再为最新的 Python 版本提供 Pydantic v1 的支持。
|
||||
|
||||
这也包括 `pydantic.v1`,在 Python 3.14 及更高版本中不再受支持。
|
||||
|
||||
如果你想使用 Python 的最新特性,需要确保使用 Pydantic v2。
|
||||
|
||||
///
|
||||
|
||||
如果你的旧 FastAPI 应用在用 Pydantic v1,这里将向你展示如何迁移到 Pydantic v2,以及 FastAPI 0.119.0 中可帮助你渐进式迁移的功能。
|
||||
|
||||
## 官方指南 { #official-guide }
|
||||
|
||||
Pydantic 有一份从 v1 迁移到 v2 的官方 <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">迁移指南</a>。
|
||||
|
||||
其中包含变更内容、校验如何更准确更严格、可能的注意事项等。
|
||||
|
||||
你可以阅读以更好地了解变更。
|
||||
|
||||
## 测试 { #tests }
|
||||
|
||||
请确保你的应用有[测试](../tutorial/testing.md){.internal-link target=_blank},并在持续集成(CI)中运行它们。
|
||||
|
||||
这样你就可以升级并确保一切仍按预期工作。
|
||||
|
||||
## `bump-pydantic` { #bump-pydantic }
|
||||
|
||||
在很多情况下,如果你使用的是未做自定义的常规 Pydantic 模型,可以将从 Pydantic v1 迁移到 v2 的大部分过程自动化。
|
||||
|
||||
你可以使用同一 Pydantic 团队提供的 <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a>。
|
||||
|
||||
该工具会帮助你自动修改大部分需要变更的代码。
|
||||
|
||||
之后运行测试检查是否一切正常。如果正常,你就完成了。😎
|
||||
|
||||
## v2 中的 Pydantic v1 { #pydantic-v1-in-v2 }
|
||||
|
||||
Pydantic v2 以子模块 `pydantic.v1` 的形式包含了 Pydantic v1 的全部内容。但在 Python 3.13 以上的版本中不再受支持。
|
||||
|
||||
这意味着你可以安装最新的 Pydantic v2,并从该子模块导入并使用旧的 Pydantic v1 组件,就像安装了旧版 Pydantic v1 一样。
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
|
||||
|
||||
### FastAPI 对 v2 中 Pydantic v1 的支持 { #fastapi-support-for-pydantic-v1-in-v2 }
|
||||
|
||||
自 FastAPI 0.119.0 起,FastAPI 也对 Pydantic v2 内的 Pydantic v1 提供了部分支持,以便迁移到 v2。
|
||||
|
||||
因此,你可以将 Pydantic 升级到最新的 v2,并将导入改为使用 `pydantic.v1` 子模块,在很多情况下就能直接工作。
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
请注意,由于 Pydantic 团队自 Python 3.14 起不再在较新的 Python 版本中支持 Pydantic v1,使用 `pydantic.v1` 在 Python 3.14 及更高版本中也不受支持。
|
||||
|
||||
///
|
||||
|
||||
### 同一应用中同时使用 Pydantic v1 与 v2 { #pydantic-v1-and-v2-on-the-same-app }
|
||||
|
||||
Pydantic 不支持在一个 Pydantic v2 模型的字段中定义 Pydantic v1 模型,反之亦然。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "❌ Not Supported"
|
||||
direction TB
|
||||
subgraph V2["Pydantic v2 Model"]
|
||||
V1Field["Pydantic v1 Model"]
|
||||
end
|
||||
subgraph V1["Pydantic v1 Model"]
|
||||
V2Field["Pydantic v2 Model"]
|
||||
end
|
||||
end
|
||||
|
||||
style V2 fill:#f9fff3
|
||||
style V1 fill:#fff6f0
|
||||
style V1Field fill:#fff6f0
|
||||
style V2Field fill:#f9fff3
|
||||
```
|
||||
|
||||
...但是,你可以在同一个应用中分别使用 Pydantic v1 和 v2 的独立模型。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "✅ Supported"
|
||||
direction TB
|
||||
subgraph V2["Pydantic v2 Model"]
|
||||
V2Field["Pydantic v2 Model"]
|
||||
end
|
||||
subgraph V1["Pydantic v1 Model"]
|
||||
V1Field["Pydantic v1 Model"]
|
||||
end
|
||||
end
|
||||
|
||||
style V2 fill:#f9fff3
|
||||
style V1 fill:#fff6f0
|
||||
style V1Field fill:#fff6f0
|
||||
style V2Field fill:#f9fff3
|
||||
```
|
||||
|
||||
在某些情况下,甚至可以在 FastAPI 应用的同一个路径操作中同时使用 Pydantic v1 和 v2 模型:
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
|
||||
|
||||
在上面的示例中,输入模型是 Pydantic v1 模型,输出模型(在 `response_model=ItemV2` 中定义)是 Pydantic v2 模型。
|
||||
|
||||
### Pydantic v1 参数 { #pydantic-v1-parameters }
|
||||
|
||||
如果你需要在 Pydantic v1 模型中使用 FastAPI 特有的参数工具,如 `Body`、`Query`、`Form` 等,在完成向 Pydantic v2 的迁移前,可以从 `fastapi.temp_pydantic_v1_params` 导入它们:
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
|
||||
|
||||
### 分步迁移 { #migrate-in-steps }
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
优先尝试 `bump-pydantic`,如果测试通过且可行,那么你就用一个命令完成了。✨
|
||||
|
||||
///
|
||||
|
||||
如果 `bump-pydantic` 不适用于你的场景,你可以在同一应用中同时支持 Pydantic v1 和 v2 模型,逐步迁移到 Pydantic v2。
|
||||
|
||||
你可以首先将 Pydantic 升级到最新的 v2,并将所有模型的导入改为使用 `pydantic.v1`。
|
||||
|
||||
然后按模块或分组,逐步把模型从 Pydantic v1 迁移到 v2。🚶
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
# 是否为输入和输出分别生成 OpenAPI JSON Schema { #separate-openapi-schemas-for-input-and-output-or-not }
|
||||
|
||||
自从发布了 **Pydantic v2**,生成的 OpenAPI 比之前更精确、更**正确**了。😎
|
||||
|
||||
事实上,在某些情况下,对于同一个 Pydantic 模型,OpenAPI 中会根据是否带有**默认值**,为输入和输出分别生成**两个 JSON Schema**。
|
||||
|
||||
我们来看看它如何工作,以及在需要时如何修改。
|
||||
|
||||
## 用于输入和输出的 Pydantic 模型 { #pydantic-models-for-input-and-output }
|
||||
|
||||
假设你有一个带有默认值的 Pydantic 模型,例如:
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
|
||||
|
||||
### 输入用的模型 { #model-for-input }
|
||||
|
||||
如果你像下面这样把该模型用作输入:
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
|
||||
|
||||
...那么 `description` 字段将**不是必填项**,因为它的默认值是 `None`。
|
||||
|
||||
### 文档中的输入模型 { #input-model-in-docs }
|
||||
|
||||
你可以在文档中确认,`description` 字段没有**红色星号**,也就是未被标记为必填:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
|
||||
</div>
|
||||
|
||||
### 输出用的模型 { #model-for-output }
|
||||
|
||||
但如果你把同一个模型用作输出,例如:
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
|
||||
|
||||
...那么因为 `description` 有默认值,即使你**不返回该字段**,它仍然会有这个**默认值**。
|
||||
|
||||
### 输出响应数据的模型 { #model-for-output-response-data }
|
||||
|
||||
如果你在文档中交互并查看响应,即使代码没有给某个 `description` 字段赋值,JSON 响应中仍包含默认值(`null`):
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
|
||||
</div>
|
||||
|
||||
这意味着它**总会有值**,只是有时该值可能为 `None`(在 JSON 中是 `null`)。
|
||||
|
||||
这也意味着,使用你的 API 的客户端无需检查该值是否存在,他们可以**假设该字段总会存在**,只是有时它会是默认值 `None`。
|
||||
|
||||
在 OpenAPI 中描述这一点的方式,是把该字段标记为**必填**,因为它总会存在。
|
||||
|
||||
因此,一个模型的 JSON Schema 会根据它用于**输入还是输出**而有所不同:
|
||||
|
||||
- 用于**输入**时,`description` **不是必填**
|
||||
- 用于**输出**时,它是**必填**(并且可能为 `None`,在 JSON 中为 `null`)
|
||||
|
||||
### 文档中的输出模型 { #model-for-output-in-docs }
|
||||
|
||||
你也可以在文档中查看输出模型,`name` 和 `description` **都**被**红色星号**标记为**必填**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
|
||||
</div>
|
||||
|
||||
### 文档中的输入/输出模型 { #model-for-input-and-output-in-docs }
|
||||
|
||||
如果你查看 OpenAPI 中可用的所有 Schema(JSON Schema),你会看到有两个,一个是 `Item-Input`,一个是 `Item-Output`。
|
||||
|
||||
对于 `Item-Input`,`description` **不是必填**,没有红色星号。
|
||||
|
||||
但对于 `Item-Output`,`description` 是**必填**,带有红色星号。
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image04.png">
|
||||
</div>
|
||||
|
||||
借助 **Pydantic v2** 的这个特性,你的 API 文档会更**精确**,如果你有自动生成的客户端和 SDK,它们也会更精确,带来更好的**开发者体验**和一致性。🎉
|
||||
|
||||
## 不要分离 Schema { #do-not-separate-schemas }
|
||||
|
||||
当然,在某些情况下,你可能希望**输入和输出使用同一个 schema**。
|
||||
|
||||
最常见的情形是:你已经有一些自动生成的客户端代码/SDK,你暂时不想更新所有这些自动生成的客户端代码/SDK(也许未来会,但不是现在)。
|
||||
|
||||
这种情况下,你可以在 **FastAPI** 中通过参数 `separate_input_output_schemas=False` 禁用该特性。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
对 `separate_input_output_schemas` 的支持是在 FastAPI `0.102.0` 中添加的。🤓
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
|
||||
|
||||
### 文档中输入/输出使用同一 Schema 的模型 { #same-schema-for-input-and-output-models-in-docs }
|
||||
|
||||
现在该模型的输入和输出将只使用一个 schema,即 `Item`,并且其中的 `description` **不是必填**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
|
||||
</div>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# 测试数据库 { #testing-a-database }
|
||||
|
||||
你可以在 <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel 文档</a> 中学习数据库、SQL 和 SQLModel。🤓
|
||||
|
||||
这里有一个关于在 FastAPI 中使用 SQLModel 的小教程:<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">使用 SQLModel 搭配 FastAPI 的教程</a>。✨
|
||||
|
||||
该教程包含一个关于 <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">测试 SQL 数据库</a> 的章节。😎
|
||||
|
|
@ -40,7 +40,7 @@ FastAPI 是一个用于构建 API 的现代、快速(高性能)的 Web 框
|
|||
* **快速**:极高性能,可与 **NodeJS** 和 **Go** 并肩(归功于 Starlette 和 Pydantic)。[最快的 Python 框架之一](#performance)。
|
||||
* **高效编码**:功能开发速度提升约 200% ~ 300%。*
|
||||
* **更少 bug**:人为(开发者)错误减少约 40%。*
|
||||
* **直观**:极佳的编辑器支持。处处皆可<abbr title="也被称为:自动完成、自动补全、IntelliSense">自动补全</abbr>。更少的调试时间。
|
||||
* **直观**:极佳的编辑器支持。处处皆可<dfn title="也被称为:自动完成、自动补全、IntelliSense">自动补全</dfn>。更少的调试时间。
|
||||
* **易用**:为易用和易学而设计。更少的文档阅读时间。
|
||||
* **简短**:最小化代码重复。一次参数声明即可获得多种功能。更少的 bug。
|
||||
* **健壮**:生产可用级代码。并带有自动生成的交互式文档。
|
||||
|
|
@ -368,7 +368,7 @@ item: Item
|
|||
* 数据校验:
|
||||
* 当数据无效时自动生成清晰的错误信息。
|
||||
* 即便是多层嵌套的 JSON 对象也会进行校验。
|
||||
* 输入数据的<abbr title="也被称为:序列化、解析、编组">转换</abbr>:从网络读取到 Python 数据和类型。读取来源:
|
||||
* <dfn title="也被称为:序列化、解析、编组">转换</dfn>输入数据:从网络读取到 Python 数据和类型。读取来源:
|
||||
* JSON。
|
||||
* 路径参数。
|
||||
* 查询参数。
|
||||
|
|
@ -376,7 +376,7 @@ item: Item
|
|||
* Headers。
|
||||
* Forms。
|
||||
* Files。
|
||||
* 输出数据的<abbr title="也被称为:序列化、解析、编组">转换</abbr>:从 Python 数据和类型转换为网络数据(JSON):
|
||||
* <dfn title="也被称为:序列化、解析、编组">转换</dfn>输出数据:从 Python 数据和类型转换为网络数据(JSON):
|
||||
* 转换 Python 类型(`str`、`int`、`float`、`bool`、`list` 等)。
|
||||
* `datetime` 对象。
|
||||
* `UUID` 对象。
|
||||
|
|
@ -439,7 +439,7 @@ item: Item
|
|||
|
||||
* 来自不同位置的**参数**声明:**headers**、**cookies**、**form 字段**和**文件**。
|
||||
* 如何设置**校验约束**,如 `maximum_length` 或 `regex`。
|
||||
* 功能强大且易用的 **<abbr title="也被称为 components、resources、providers、services、injectables">依赖注入</abbr>** 系统。
|
||||
* 功能强大且易用的 **<dfn title="也被称为:组件、资源、提供者、服务、可注入项">依赖注入</dfn>** 系统。
|
||||
* 安全与认证,包括对 **OAuth2**、**JWT tokens** 和 **HTTP Basic** 认证的支持。
|
||||
* 更高级(但同样简单)的 **多层嵌套 JSON 模型** 声明技巧(得益于 Pydantic)。
|
||||
* 通过 <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> 等库进行 **GraphQL** 集成。
|
||||
|
|
@ -524,7 +524,7 @@ Starlette 使用:
|
|||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - 使用 `TestClient` 时需要。
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 使用默认模板配置时需要。
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - 使用 `request.form()` 支持表单<abbr title="将 HTTP 请求中的字符串转换为 Python 数据">「解析」</abbr>时需要。
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - 使用 `request.form()` 支持表单<dfn title="将 HTTP 请求中的字符串转换为 Python 数据">「解析」</dfn>时需要。
|
||||
|
||||
FastAPI 使用:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Python 支持可选的“类型提示”(也叫“类型注解”)。
|
||||
|
||||
这些“类型提示”或注解是一种特殊语法,用来声明变量的<abbr title="例如:str、int、float、bool">类型</abbr>。
|
||||
这些“类型提示”或注解是一种特殊语法,用来声明变量的<dfn title="例如:str、int、float、bool">类型</dfn>。
|
||||
|
||||
通过为变量声明类型,编辑器和工具可以为你提供更好的支持。
|
||||
|
||||
这只是一个关于 Python 类型提示的快速入门/复习。它只涵盖与 **FastAPI** 一起使用所需的最少部分……实际上非常少。
|
||||
这只是一个关于 Python 类型提示的快速入门/复习。它只涵盖与 **FastAPI** 一起使用所需的最少部分...实际上非常少。
|
||||
|
||||
**FastAPI** 完全基于这些类型提示构建,它们带来了许多优势和好处。
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Python 支持可选的“类型提示”(也叫“类型注解”)。
|
|||
|
||||
让我们从一个简单的例子开始:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
运行这个程序会输出:
|
||||
|
||||
|
|
@ -34,9 +34,9 @@ John Doe
|
|||
|
||||
* 接收 `first_name` 和 `last_name`。
|
||||
* 通过 `title()` 将每个参数的第一个字母转换为大写。
|
||||
* 用一个空格将它们<abbr title="把它们合在一起成为一个,内容一个接在另一个后面。">拼接</abbr>起来。
|
||||
* 用一个空格将它们<dfn title="把它们合在一起成为一个,内容一个接在另一个后面。">拼接</dfn>起来。
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### 修改它 { #edit-it }
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ John Doe
|
|||
|
||||
这些就是“类型提示”:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
这和声明默认值不同,比如:
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ John Doe
|
|||
|
||||
看这个已经带有类型提示的函数:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
因为编辑器知道变量的类型,你不仅能得到补全,还能获得错误检查:
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ John Doe
|
|||
|
||||
现在你知道需要修复它,用 `str(age)` 把 `age` 转成字符串:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## 声明类型 { #declaring-types }
|
||||
|
||||
|
|
@ -133,29 +133,32 @@ John Doe
|
|||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### 带类型参数的泛型类型 { #generic-types-with-type-parameters }
|
||||
### typing 模块 { #typing-module }
|
||||
|
||||
有些数据结构可以包含其他值,比如 `dict`、`list`、`set` 和 `tuple`。而内部的值也会有自己的类型。
|
||||
在一些额外的用例中,你可能需要从标准库的 `typing` 模块导入内容。比如当你想声明“任意类型”时,可以使用 `typing` 中的 `Any`:
|
||||
|
||||
这些带有内部类型的类型称为“泛型”(generic)类型。可以把它们连同内部类型一起声明出来。
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
要声明这些类型以及内部类型,你可以使用 Python 标准库模块 `typing`。它就是为支持这些类型提示而存在的。
|
||||
|
||||
#### 更新的 Python 版本 { #newer-versions-of-python }
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
使用 `typing` 的语法与所有版本兼容,从 Python 3.6 到最新版本(包括 Python 3.9、Python 3.10 等)。
|
||||
### 泛型类型 { #generic-types }
|
||||
|
||||
随着 Python 的发展,更新的版本对这些类型注解的支持更好,在很多情况下你甚至不需要导入和使用 `typing` 模块来声明类型注解。
|
||||
有些类型可以在方括号中接收“类型参数”(type parameters),用于声明其内部值的类型。比如“字符串列表”可以写为 `list[str]`。
|
||||
|
||||
如果你可以为项目选择更高版本的 Python,你将能享受到这种额外的简化。
|
||||
这些能接收类型参数的类型称为“泛型类型”(Generic types)或“泛型”(Generics)。
|
||||
|
||||
在整个文档中,会根据不同 Python 版本提供相应的示例(当存在差异时)。
|
||||
你可以把相同的内建类型作为泛型使用(带方括号和内部类型):
|
||||
|
||||
比如“Python 3.6+”表示兼容 Python 3.6 及以上(包括 3.7、3.8、3.9、3.10 等)。而“Python 3.9+”表示兼容 Python 3.9 及以上(包括 3.10 等)。
|
||||
|
||||
如果你可以使用最新的 Python 版本,请使用最新版本的示例,它们将拥有最简洁的语法,例如“Python 3.10+”。
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
#### 列表 { #list }
|
||||
|
||||
|
|
@ -167,7 +170,7 @@ John Doe
|
|||
|
||||
因为 list 是一种包含内部类型的类型,把内部类型写在方括号里:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -193,7 +196,7 @@ John Doe
|
|||
|
||||
声明 `tuple` 和 `set` 的方式类似:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
这表示:
|
||||
|
||||
|
|
@ -208,7 +211,7 @@ John Doe
|
|||
|
||||
第二个类型参数用于字典的值:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
这表示:
|
||||
|
||||
|
|
@ -220,44 +223,20 @@ John Doe
|
|||
|
||||
你可以声明一个变量可以是若干种类型中的任意一种,比如既可以是 `int` 也可以是 `str`。
|
||||
|
||||
在 Python 3.6 及以上(包括 Python 3.10),你可以使用 `typing` 中的 `Union`,把可能的类型放到方括号里。
|
||||
定义时使用<dfn title='也叫“按位或运算符(bitwise or operator)”,但这里与该含义无关'>竖线(`|`)</dfn>把两种类型分开。
|
||||
|
||||
在 Python 3.10 中还有一种新的语法,可以用<abbr title='也叫“按位或运算符(bitwise or operator)”,但这里与该含义无关'>竖线(`|`)</abbr>把可能的类型分隔开。
|
||||
|
||||
//// tab | Python 3.10+
|
||||
这称为“联合类型”(union),因为变量可以是这两类类型集合的并集中的任意一个。
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
两种方式的含义一致:`item` 可以是 `int` 或 `str`。
|
||||
这表示 `item` 可以是 `int` 或 `str`。
|
||||
|
||||
#### 可能为 `None` { #possibly-none }
|
||||
|
||||
你可以声明一个值的类型是某种类型(比如 `str`),但它也可能是 `None`。
|
||||
|
||||
在 Python 3.6 及以上(包括 Python 3.10),你可以通过从 `typing` 模块导入并使用 `Optional` 来声明:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
使用 `Optional[str]` 而不是仅仅 `str`,可以让编辑器帮助你发现把值当成总是 `str` 的错误(实际上它也可能是 `None`)。
|
||||
|
||||
`Optional[Something]` 实际上是 `Union[Something, None]` 的简写,它们等价。
|
||||
|
||||
这也意味着在 Python 3.10 中,你可以使用 `Something | None`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
|
|
@ -266,96 +245,7 @@ John Doe
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 另一种写法
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### 使用 `Union` 或 `Optional` { #using-union-or-optional }
|
||||
|
||||
如果你使用的是 3.10 以下的 Python 版本,这里有个来自我非常主观的建议:
|
||||
|
||||
* 🚨 避免使用 `Optional[SomeType]`
|
||||
* 改用 ✨**`Union[SomeType, None]`**✨
|
||||
|
||||
两者等价,底层相同,但我更推荐 `Union` 而不是 `Optional`,因为“optional(可选)”这个词看起来像是在说“参数可选”,而它实际上表示“它可以是 `None`”,即使它并不是可选的,仍然是必填的。
|
||||
|
||||
我认为 `Union[SomeType, None]` 更明确地表达了它的意思。
|
||||
|
||||
这只是关于词语和名字。但这些词会影响你和你的队友如何理解代码。
|
||||
|
||||
例如,看下面这个函数:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
参数 `name` 被定义为 `Optional[str]`,但它并不是“可选”的,你不能不传这个参数就调用函数:
|
||||
|
||||
```Python
|
||||
say_hi() # 哦不,这会抛错!😱
|
||||
```
|
||||
|
||||
参数 `name` 仍然是必填的(不是“可选”),因为它没有默认值。不过,`name` 接受 `None` 作为值:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # 这样可以,None 是有效值 🎉
|
||||
```
|
||||
|
||||
好消息是,一旦你使用 Python 3.10,就无需再为此操心,因为你可以直接用 `|` 来定义类型联合:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
这样你就不必再考虑 `Optional` 和 `Union` 这些名字了。😎
|
||||
|
||||
#### 泛型类型 { #generic-types }
|
||||
|
||||
这些在方括号中接收类型参数的类型称为“泛型类型”(Generic types)或“泛型”(Generics),例如:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
你可以把同样的内建类型作为泛型使用(带方括号和内部类型):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
以及与之前的 Python 版本一样,来自 `typing` 模块的:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ……以及其他。
|
||||
|
||||
在 Python 3.10 中,作为使用泛型 `Union` 和 `Optional` 的替代,你可以使用<abbr title='也叫“按位或运算符(bitwise or operator)”,但这里与该含义无关'>竖线(`|`)</abbr>来声明类型联合,这更好也更简单。
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
你可以把同样的内建类型作为泛型使用(带方括号和内部类型):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
以及来自 `typing` 模块的泛型:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ……以及其他。
|
||||
|
||||
////
|
||||
使用 `str | None` 而不是仅仅 `str`,可以让编辑器帮助你发现把值当成总是 `str` 的错误(实际上它也可能是 `None`)。
|
||||
|
||||
### 类作为类型 { #classes-as-types }
|
||||
|
||||
|
|
@ -363,11 +253,11 @@ say_hi(name=None) # 这样可以,None 是有效值 🎉
|
|||
|
||||
假设你有一个名为 `Person` 的类,带有 name:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
|
||||
|
||||
然后你可以声明一个变量是 `Person` 类型:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
接着,你会再次获得所有的编辑器支持:
|
||||
|
||||
|
|
@ -403,19 +293,13 @@ say_hi(name=None) # 这样可以,None 是有效值 🎉
|
|||
|
||||
你会在[教程 - 用户指南](tutorial/index.md){.internal-link target=_blank}中看到更多的实战示例。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
当你在没有默认值的情况下使用 `Optional` 或 `Union[Something, None]` 时,Pydantic 有一个特殊行为,你可以在 Pydantic 文档的 <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">必填的 Optional 字段</a> 中了解更多。
|
||||
|
||||
///
|
||||
|
||||
## 带元数据注解的类型提示 { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python 还提供了一个特性,可以使用 `Annotated` 在这些类型提示中放入额外的<abbr title="关于数据的数据,在这里是关于类型的信息,例如描述。">元数据</abbr>。
|
||||
Python 还提供了一个特性,可以使用 `Annotated` 在这些类型提示中放入额外的<dfn title="关于数据的数据,此处指关于类型的信息,例如描述。">元数据</dfn>。
|
||||
|
||||
从 Python 3.9 起,`Annotated` 是标准库的一部分,因此可以从 `typing` 导入。
|
||||
你可以从 `typing` 导入 `Annotated`。
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
Python 本身不会对这个 `Annotated` 做任何处理。对于编辑器和其他工具,类型仍然是 `str`。
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# 资源 { #resources }
|
||||
|
||||
更多资源、外部链接等。✈️
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/// details | 🌐 由 AI 与人类协作翻译
|
||||
|
||||
本翻译由人类引导的 AI 生成。🤝
|
||||
|
||||
可能存在误解原意或不够自然等问题。🤖
|
||||
|
||||
你可以通过[帮助我们更好地引导 AI LLM](https://fastapi.tiangolo.com/zh/contributing/#translations)来改进此翻译。
|
||||
|
||||
[英文版本](ENGLISH_VERSION_URL)
|
||||
|
||||
///
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
首先导入 `BackgroundTasks` 并在 *路径操作函数* 中使用类型声明 `BackgroundTasks` 定义一个参数:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
|
||||
|
||||
**FastAPI** 会创建一个 `BackgroundTasks` 类型的对象并作为该参数传入。
|
||||
|
||||
|
|
@ -31,13 +31,13 @@
|
|||
|
||||
由于写操作不使用 `async` 和 `await`,我们用普通的 `def` 定义函数:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## 添加后台任务 { #add-the-background-task }
|
||||
|
||||
在你的 *路径操作函数* 里,用 `.add_task()` 方法将任务函数传到 *后台任务* 对象中:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` 接收以下参数:
|
||||
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
在FastAPI中仍然可以单独使用 `BackgroundTask`,但您必须在代码中创建对象,并返回包含它的Starlette `Response`。
|
||||
|
||||
更多细节查看 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">Starlette's official docs for Background Tasks</a>.
|
||||
更多细节查看 <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">Starlette 后台任务的官方文档</a>.
|
||||
|
||||
## 告诫 { #caveat }
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ from app.routers import items
|
|||
|
||||
你可以导入它并通过与 `FastAPI` 类相同的方式创建一个「实例」:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### 使用 `APIRouter` 的*路径操作* { #path-operations-with-apirouter }
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ from app.routers import items
|
|||
|
||||
使用方式与 `FastAPI` 类相同:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
你可以将 `APIRouter` 视为一个「迷你 `FastAPI`」类。
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ from app.routers import items
|
|||
|
||||
现在我们将使用一个简单的依赖项来读取一个自定义的 `X-Token` 请求首部:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ from app.routers import items
|
|||
|
||||
因此,我们可以将其添加到 `APIRouter` 中,而不是将其添加到每个路径操作中。
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
由于每个*路径操作*的路径都必须以 `/` 开头,例如:
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ async def read_item(item_id: str):
|
|||
|
||||
因此,我们通过 `..` 对依赖项使用了相对导入:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### 相对导入如何工作 { #how-relative-imports-work }
|
||||
|
||||
|
|
@ -279,11 +279,11 @@ from ...dependencies import get_token_header
|
|||
|
||||
但是我们仍然可以添加*更多*将会应用于特定的*路径操作*的 `tags`,以及一些特定于该*路径操作*的额外 `responses`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
最后的这个路径操作将包含标签的组合:`["items","custom"]`。
|
||||
最后的这个路径操作将包含标签的组合:`["items", "custom"]`。
|
||||
|
||||
并且在文档中也会有两个响应,一个用于 `404`,一个用于 `403`。
|
||||
|
||||
|
|
@ -305,13 +305,13 @@ from ...dependencies import get_token_header
|
|||
|
||||
我们甚至可以声明[全局依赖项](dependencies/global-dependencies.md){.internal-link target=_blank},它会和每个 `APIRouter` 的依赖项组合在一起:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### 导入 `APIRouter` { #import-the-apirouter }
|
||||
|
||||
现在,我们导入具有 `APIRouter` 的其他子模块:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
{* ../../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` 一个部分的子模块,因此我们可以使用单个点 ` .` 通过「相对导入」来导入它们。
|
||||
|
||||
|
|
@ -374,13 +374,13 @@ from .routers.users import router
|
|||
|
||||
因此,为了能够在同一个文件中使用它们,我们直接导入子模块:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### 包含 `users` 和 `items` 的 `APIRouter` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
现在,让我们来包含来自 `users` 和 `items` 子模块的 `router`。
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -420,13 +420,13 @@ from .routers.users import router
|
|||
|
||||
对于此示例,它将非常简单。但是假设由于它是与组织中的其他项目所共享的,因此我们无法对其进行修改,以及直接在 `APIRouter` 中添加 `prefix`、`dependencies`、`tags` 等:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
但是我们仍然希望在包含 `APIRouter` 时设置一个自定义的 `prefix`,以便其所有*路径操作*以 `/admin` 开头,我们希望使用本项目已经有的 `dependencies` 保护它,并且我们希望它包含自定义的 `tags` 和 `responses`。
|
||||
|
||||
我们可以通过将这些参数传递给 `app.include_router()` 来完成所有的声明,而不必修改原始的 `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
这样,原始的 `APIRouter` 将保持不变,因此我们仍然可以与组织中的其他项目共享相同的 `app/internal/admin.py` 文件。
|
||||
|
||||
|
|
@ -447,7 +447,7 @@ from .routers.users import router
|
|||
|
||||
这里我们这样做了...只是为了表明我们可以做到🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
它将与通过 `app.include_router()` 添加的所有其他*路径操作*一起正常运行。
|
||||
|
||||
|
|
|
|||
|
|
@ -104,12 +104,6 @@
|
|||
q: str | None = None
|
||||
```
|
||||
|
||||
或者在 Python 3.9 中:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
比如:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ images: list[Image]
|
|||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
|
||||
|
||||
## 无处不在的编辑器支持 { #editor-support-everywhere }
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ images: list[Image]
|
|||
|
||||
在下面的例子中,你将接受任意键为 `int` 类型并且值为 `float` 类型的 `dict`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
|
||||
FastAPI 会根据默认值 `= None` 知道 `q` 的值不是必填的。
|
||||
|
||||
`str | None`(Python 3.10+)或 `Union[str, None]`(Python 3.9+ 中的 `Union`)并不是 FastAPI 用来判断是否必填的依据;是否必填由是否有默认值 `= None` 决定。
|
||||
`str | None` 并不是 FastAPI 用来判断是否必填的依据;是否必填由是否有默认值 `= None` 决定。
|
||||
|
||||
但添加这些类型注解可以让你的编辑器提供更好的支持并检测错误。
|
||||
|
||||
|
|
|
|||
|
|
@ -46,17 +46,17 @@
|
|||
|
||||
在某些特殊使用情况下(可能并不常见),您可能希望**限制**您想要接收的 cookie。
|
||||
|
||||
您的 API 现在可以控制自己的 <abbr title="顺带一提,这是一个笑话。它与 cookie 同意无关,但现在连API都能拒绝那些可怜的 cookie,真是太有意思了。来,吃块小饼干(cookie)吧。🍪">cookie 同意</abbr>。🤪🍪
|
||||
您的 API 现在可以控制自己的 <dfn title="只是个玩笑,别当真。和 cookie 同意无关,不过连 API 现在都能拒绝可怜的 cookie,挺好玩的。来,吃块小饼干吧。🍪">cookie 同意</dfn>。🤪🍪
|
||||
|
||||
您可以使用 Pydantic 的模型配置来禁止( `forbid` )任何额外( `extra` )字段:
|
||||
|
||||
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
|
||||
|
||||
如果客户尝试发送一些**额外的 cookie**,他们将收到**错误**响应。
|
||||
如果客户端尝试发送一些**额外的 cookie**,他们将收到**错误**响应。
|
||||
|
||||
可怜的 cookie 通知条,费尽心思为了获得您的同意,却被<abbr title="这又是一个笑话,别管我了,给您的小饼干(cookie)配上点咖啡吧。☕">API 拒绝了</abbr>。🍪
|
||||
可怜的 cookie 通知条,费尽心思为了获得您的同意,却被<dfn title="又是个玩笑,别理我。给你的小饼干配点咖啡吧。☕">API 拒绝了</dfn>。🍪
|
||||
|
||||
例如,如果客户端尝试发送一个值为 `good-list-please` 的 `santa_tracker` cookie,客户端将收到一个**错误**响应,告知他们 `santa_tracker` <abbr title="圣诞老人(Santa)不赞成没有小饼干(cookie)。🎅 好吧,不会再开 cookie 的玩笑了。">cookie 是不允许的</abbr>:
|
||||
例如,如果客户端尝试发送一个值为 `good-list-please` 的 `santa_tracker` cookie,客户端将收到一个**错误**响应,告知他们 `santa_tracker` <dfn title="圣诞老人不赞成没有小饼干。🎅 好吧,不再讲 cookie 的笑话了。">cookie 是不允许的</dfn>:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -73,4 +73,4 @@
|
|||
|
||||
## 总结 { #summary }
|
||||
|
||||
您可以使用 **Pydantic 模型**在 **FastAPI** 中声明 <abbr title="走之前再来块小饼干吧。 🍪">**cookie**</abbr>。😎
|
||||
您可以使用 **Pydantic 模型**在 **FastAPI** 中声明 <dfn title="临走前再吃一块小饼干吧。🍪">**cookie**</dfn>。😎
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
* 特定的 HTTP 方法(`POST`,`PUT`)或者使用通配符 `"*"` 允许所有方法。
|
||||
* 特定的 HTTP 请求头或者使用通配符 `"*"` 允许所有请求头。
|
||||
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
|
||||
|
||||
默认情况下,这个 `CORSMiddleware` 实现所使用的默认参数较为保守,所以你需要显式地启用特定的源、方法或者 headers,以便浏览器能够在跨域上下文中使用它们。
|
||||
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
* `expose_headers` - 指示可以被浏览器访问的响应头。默认为 `[]`。
|
||||
* `max_age` - 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 `600`。
|
||||
|
||||
中间件响应两种特定类型的 HTTP 请求……
|
||||
中间件响应两种特定类型的 HTTP 请求...
|
||||
|
||||
### CORS 预检请求 { #cors-preflight-requests }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
在你的 FastAPI 应用中直接导入 `uvicorn` 并运行:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### 关于 `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ from myapp import app
|
|||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
/// info
|
||||
/// info | 信息
|
||||
|
||||
更多信息请检查 <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python 官方文档</a>.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
对此,我们可以做的更好...
|
||||
|
||||
## 什么构成了依赖项? { #what-makes-a-dependency }
|
||||
## 什么构成了依赖项 { #what-makes-a-dependency }
|
||||
|
||||
到目前为止,你看到的依赖项都被声明为函数。
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ fluffy = Cat(name="Mr Fluffy")
|
|||
|
||||
注意,我们在上面的代码中编写了两次`CommonQueryParams`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
|||
... Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...实际上是 **Fastapi** 用来知道依赖项是什么的。
|
||||
...实际上是 **FastAPI** 用来知道依赖项是什么的。
|
||||
|
||||
FastAPI 将从依赖项中提取声明的参数,这才是 FastAPI 实际调用的。
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ FastAPI 将从依赖项中提取声明的参数,这才是 FastAPI 实际调用
|
|||
|
||||
在本例中,第一个 `CommonQueryParams` :
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
|
|
@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
|||
|
||||
你实际上可以只这样编写:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
|
@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ commons = Depends(CommonQueryParams)
|
|||
|
||||
但是你可以看到,我们在这里有一些代码重复了,编写了`CommonQueryParams`两次:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
|||
|
||||
不是写成这样:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
|||
|
||||
...而是这样写:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
|
@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 未使用 Annotated
|
||||
//// tab | Python 3.10+ 未使用 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
该参数的值是由 `Depends()` 组成的 `list`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
|
||||
|
||||
路径操作装饰器依赖项的执行或解析方式和普通依赖项一样,但就算这些依赖项会返回值,它们的值也不会传递给*路径操作函数*。
|
||||
|
||||
|
|
@ -22,13 +22,13 @@
|
|||
|
||||
有些编辑器会检查代码中没使用过的函数参数,并显示错误提示。
|
||||
|
||||
在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器显示错误提示。
|
||||
在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器/工具报错。
|
||||
|
||||
使用路径装饰器依赖项还可以避免开发新人误会代码中包含无用的未使用参数。
|
||||
|
||||
///
|
||||
|
||||
/// info | 说明
|
||||
/// info | 信息
|
||||
|
||||
本例中,使用的是自定义响应头 `X-Key` 和 `X-Token`。
|
||||
|
||||
|
|
@ -44,13 +44,13 @@
|
|||
|
||||
路径装饰器依赖项可以声明请求的需求项(比如响应头)或其他子依赖项:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
|
||||
|
||||
### 触发异常 { #raise-exceptions }
|
||||
|
||||
路径装饰器依赖项与正常的依赖项一样,可以 `raise` 异常:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
|
||||
|
||||
### 返回值 { #return-values }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
因此,可以复用在其他位置使用过的、(能返回值的)普通依赖项,即使没有使用这个值,也会执行该依赖项:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
|
||||
|
||||
## 为一组路径操作定义依赖项 { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 使用 yield 的依赖项 { #dependencies-with-yield }
|
||||
|
||||
FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exit code"、"cleanup code"、"teardown code"、"closing code"、"context manager exit code" 等'>额外步骤</abbr>的依赖项。
|
||||
FastAPI 支持那些在完成后执行一些<dfn title="有时也称为:“退出代码”、“清理代码”、“拆卸代码”、“关闭代码”、“上下文管理器退出代码”等">额外步骤</dfn>的依赖项。
|
||||
|
||||
为此,使用 `yield` 而不是 `return`,并把这些额外步骤(代码)写在后面。
|
||||
|
||||
|
|
@ -29,15 +29,15 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
在创建响应之前,只会执行 `yield` 语句及其之前的代码:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
|
||||
|
||||
`yield` 产生的值会注入到 *路径操作* 和其他依赖项中:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
|
||||
|
||||
`yield` 语句后面的代码会在响应之后执行:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
同样地,你可以使用 `finally` 来确保退出步骤一定会被执行,无论是否发生异常。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
|
||||
|
||||
## 使用 `yield` 的子依赖项 { #sub-dependencies-with-yield }
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
例如,`dependency_c` 可以依赖 `dependency_b`,而 `dependency_b` 则依赖 `dependency_a`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
|
||||
|
||||
并且它们都可以使用 `yield`。
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
而 `dependency_b` 又需要 `dependency_a`(此处命名为 `dep_a`)的值在其退出代码中可用。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
|
||||
|
||||
同样地,你可以将一些依赖用 `yield`,另一些用 `return`,并让其中一些依赖依赖于另一些。
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
|
||||
|
||||
如果你想捕获异常并基于它创建一个自定义响应,请创建一个[自定义异常处理器](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}。
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
如果你在带有 `yield` 的依赖中使用 `except` 捕获了一个异常,并且你没有再次抛出它(或抛出一个新异常),FastAPI 将无法察觉发生过异常,就像普通的 Python 代码那样:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
|
||||
|
||||
在这种情况下,客户端会像预期那样看到一个 *HTTP 500 Internal Server Error* 响应,因为我们没有抛出 `HTTPException` 或类似异常,但服务器将**没有任何日志**或其他关于错误是什么的提示。😱
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ FastAPI 支持那些在完成后执行一些<abbr title='有时也被称为 "exi
|
|||
|
||||
你可以使用 `raise` 重新抛出同一个异常:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
|
||||
|
||||
现在客户端仍会得到同样的 *HTTP 500 Internal Server Error* 响应,但服务器日志中会有我们自定义的 `InternalError`。😎
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ participant tasks as Background tasks
|
|||
|
||||
但如果你知道在从 *路径操作函数* 返回之后不再需要使用该依赖,你可以使用 `Depends(scope="function")` 告诉 FastAPI:应当在 *路径操作函数* 返回后、但在**响应发送之前**关闭该依赖。
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
|
||||
|
||||
`Depends()` 接收一个 `scope` 参数,可为:
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ with open("./somefile.txt") as f:
|
|||
|
||||
你也可以在 **FastAPI** 的带有 `yield` 的依赖中,使用依赖函数内部的 `with` 或 `async with` 语句来使用它们:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
这样一来,就可以为所有*路径操作*应用该依赖项:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
|
||||
|
||||
[将 `dependencies` 添加到*路径操作装饰器*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} 一章的思路均适用于全局依赖项, 在本例中,这些依赖项可以用于应用中的所有*路径操作*。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 依赖项 { #dependencies }
|
||||
|
||||
**FastAPI** 提供了简单直观但功能强大的**<abbr title="也称为组件、资源、提供者、服务、可注入项">依赖注入</abbr>**系统。
|
||||
**FastAPI** 提供了简单直观但功能强大的**<dfn title="也称为:组件、资源、提供者、服务、可注入项">依赖注入</dfn>**系统。
|
||||
|
||||
它被设计得非常易用,能让任何开发者都能轻松把其他组件与 **FastAPI** 集成。
|
||||
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
大功告成。
|
||||
|
||||
只用了**2 行**代码。
|
||||
**2 行**。
|
||||
|
||||
它的形式和结构与所有*路径操作函数*相同。
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ commons: Annotated[dict, Depends(common_parameters)]
|
|||
* 响应数据注入系统
|
||||
* 等等...
|
||||
|
||||
## 簡单而强大 { #simple-and-powerful }
|
||||
## 简单而强大 { #simple-and-powerful }
|
||||
|
||||
虽然**层级式依赖注入系统**的定义与使用非常简单,但它依然非常强大。
|
||||
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
|||
|
||||
如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。
|
||||
|
||||
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行<abbr title="一个实用程序/系统来存储计算/生成的值,以便重用它们,而不是再次计算它们。">「缓存」</abbr>,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
|
||||
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行<dfn title="用于存储已计算/生成的值,以便复用而无需再次计算的实用工具/系统">「缓存」</dfn>,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
|
||||
|
||||
在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
|
|
@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ 非 Annotated
|
||||
//// tab | Python 3.10+ 非 Annotated
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
例如,它不接收`datetime`这类的对象,因为这些对象与JSON不兼容。
|
||||
|
||||
因此,`datetime`对象必须将转换为包含<a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO格式化</a>的`str`类型对象。
|
||||
因此,`datetime`对象必须转换为包含<a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO 格式</a>的`str`类型对象。
|
||||
|
||||
同样,这个数据库也不会接收Pydantic模型(带有属性的对象),而只接收`dict`。
|
||||
|
||||
|
|
@ -30,6 +30,6 @@
|
|||
|
||||
/// note | 注意
|
||||
|
||||
`jsonable_encoder`实际上是FastAPI内部用来转换数据的。但是它在许多其他场景中也很有用。
|
||||
`jsonable_encoder`实际上是**FastAPI**内部用来转换数据的。但是它在许多其他场景中也很有用。
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* **输出模型**不应含密码
|
||||
* **数据库模型**可能需要包含哈希后的密码
|
||||
|
||||
/// danger
|
||||
/// danger | 危险
|
||||
|
||||
不要存储用户的明文密码。始终只存储之后可用于校验的“安全哈希”。
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ UserInDB(
|
|||
)
|
||||
```
|
||||
|
||||
/// warning
|
||||
/// warning | 警告
|
||||
|
||||
配套的辅助函数 `fake_password_hasher` 和 `fake_save_user` 仅用于演示可能的数据流,当然并不提供真实的安全性。
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ UserInDB(
|
|||
|
||||
为此,请使用 Python 标准类型提示 <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
|
||||
|
||||
/// note
|
||||
/// note | 注意
|
||||
|
||||
定义 <a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a> 类型时,要把更具体的类型写在前面,然后是不太具体的类型。下例中,更具体的 `PlaneItem` 位于 `Union[PlaneItem, CarItem]` 中的 `CarItem` 之前。
|
||||
|
||||
|
|
@ -190,9 +190,9 @@ some_variable: PlaneItem | CarItem
|
|||
|
||||
同样地,你可以声明由对象列表构成的响应。
|
||||
|
||||
为此,请使用标准的 Python `typing.List`(在 Python 3.9+ 中也可以直接用 `list`):
|
||||
为此,请使用标准的 Python `list`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
## 任意 `dict` 的响应 { #response-with-arbitrary-dict }
|
||||
|
||||
|
|
@ -200,9 +200,9 @@ some_variable: PlaneItem | CarItem
|
|||
|
||||
如果你事先不知道有效的字段/属性名(Pydantic 模型需要预先知道字段)时,这很有用。
|
||||
|
||||
此时,可以使用 `typing.Dict`(在 Python 3.9+ 中也可以直接用 `dict`):
|
||||
此时,可以使用 `dict`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
|
||||
|
||||
## 小结 { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
最简单的 FastAPI 文件可能像下面这样:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
将其复制到 `main.py` 文件中。
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ OpenAPI 为你的 API 定义 API 模式。该模式中包含了你的 API 发送
|
|||
|
||||
如果你对原始的 OpenAPI 模式长什么样子感到好奇,FastAPI 自动生成了包含所有 API 描述的 JSON(模式)。
|
||||
|
||||
你可以直接在:<a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:800api.json</a> 看到它。
|
||||
你可以直接在:<a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a> 看到它。
|
||||
|
||||
它将显示以如下内容开头的 JSON:
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Deploying to FastAPI Cloud...
|
|||
|
||||
### 步骤 1:导入 `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` 是一个为你的 API 提供了所有功能的 Python 类。
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ Deploying to FastAPI Cloud...
|
|||
|
||||
### 步骤 2:创建一个 `FastAPI`「实例」 { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
这里的变量 `app` 会是 `FastAPI` 类的一个「实例」。
|
||||
|
||||
|
|
@ -266,12 +266,12 @@ https://example.com/items/foo
|
|||
|
||||
#### 定义一个*路径操作装饰器* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`@app.get("/")` 告诉 **FastAPI** 在它下方的函数负责处理如下访问请求:
|
||||
|
||||
* 请求路径为 `/`
|
||||
* 使用 <abbr title="HTTP GET 方法"><code>get</code> 操作</abbr>
|
||||
* 使用 <dfn title="一种 HTTP GET 方法"><code>get</code> 操作</dfn>
|
||||
|
||||
/// info | `@decorator` Info
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ https://example.com/items/foo
|
|||
* **操作**:是 `get`。
|
||||
* **函数**:是位于「装饰器」下方的函数(位于 `@app.get("/")` 下方)。
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
这是一个 Python 函数。
|
||||
|
||||
|
|
@ -332,7 +332,7 @@ https://example.com/items/foo
|
|||
|
||||
你也可以将其定义为常规函数而不使用 `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ https://example.com/items/foo
|
|||
|
||||
### 步骤 5:返回内容 { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
你可以返回一个 `dict`、`list`,像 `str`、`int` 一样的单个值,等等。
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
### 导入 `HTTPException` { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
|
||||
|
||||
### 在代码中触发 `HTTPException` { #raise-an-httpexception-in-your-code }
|
||||
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
本例中,客户端用不存在的 `ID` 请求 `item` 时,触发状态码为 `404` 的异常:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
|
||||
|
||||
### 响应结果 { #the-resulting-response }
|
||||
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
但在某些高级场景中需要时,你可以添加自定义响应头:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
|
||||
|
||||
## 安装自定义异常处理器 { #install-custom-exception-handlers }
|
||||
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
此时,可以用 `@app.exception_handler()` 添加自定义异常处理器:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
|
||||
|
||||
这里,请求 `/unicorns/yolo` 时,路径操作会触发 `UnicornException`。
|
||||
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
|
||||
异常处理器会接收 `Request` 和该异常。
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
|
||||
|
||||
现在,访问 `/items/foo` 时,默认的 JSON 错误为:
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
|
|||
|
||||
例如,只为这些错误返回纯文本响应,而不是 JSON:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
|
|||
|
||||
开发时,你可以用它来记录请求体、调试错误,或返回给用户等。
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
|
||||
|
||||
现在试着发送一个无效的 `item`,例如:
|
||||
|
||||
|
|
@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
|||
|
||||
如果你想在自定义处理后仍复用 **FastAPI** 的默认异常处理器,可以从 `fastapi.exception_handlers` 导入并复用这些默认处理器:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
|
||||
|
||||
虽然本例只是用非常夸张的信息打印了错误,但足以说明:你可以先处理异常,然后再复用默认的异常处理器。
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
你可以按如下方式设置它们:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
|
||||
|
||||
## 标签元数据 { #metadata-for-tags }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
创建标签元数据并把它传递给 `openapi_tags` 参数:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
|
||||
|
||||
注意你可以在描述内使用 Markdown,例如「login」会显示为粗体(**login**)以及「fancy」会显示为斜体(_fancy_)。
|
||||
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
将 `tags` 参数和*路径操作*(以及 `APIRouter`)一起使用,将其分配给不同的标签:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
|
||||
例如,将其设置为服务于 `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
|
||||
|
||||
如果你想完全禁用 OpenAPI 模式,可以将其设置为 `openapi_url=None`,这样也会禁用使用它的文档用户界面。
|
||||
|
||||
|
|
@ -117,4 +117,4 @@
|
|||
|
||||
例如,设置 Swagger UI 服务于 `/documentation` 并禁用 ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
* 然后它返回由相应*路径操作*生成的 `response`。
|
||||
* 在返回之前,你可以进一步修改 `response`。
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
例如,你可以添加一个自定义请求头 `X-Process-Time`,其值为处理请求并生成响应所花费的秒数:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ OpenAPI 概图会自动添加标签,供 API 文档接口使用:
|
|||
|
||||
**FastAPI** 对此的支持与使用普通字符串相同:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
|
||||
|
||||
## 摘要和描述 { #summary-and-description }
|
||||
|
||||
|
|
@ -56,13 +56,13 @@ OpenAPI 概图会自动添加标签,供 API 文档接口使用:
|
|||
|
||||
## 从 docstring 获取描述 { #description-from-docstring }
|
||||
|
||||
描述内容比较长且占用多行时,可以在函数的 <abbr title="作为函数内部的第一个表达式、用于文档用途的多行字符串(不赋给任何变量)">docstring</abbr> 中声明*路径操作*的描述,**FastAPI** 会从中读取。
|
||||
描述内容比较长且占用多行时,可以在函数的 <dfn title="作为函数内部的第一个表达式(不赋给任何变量)的多行字符串,用于文档用途">docstring</dfn> 中声明*路径操作*的描述,**FastAPI** 会从中读取。
|
||||
|
||||
文档字符串支持 <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a>,能正确解析和显示 Markdown 的内容,但要注意文档字符串的缩进。
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
|
||||
|
||||
下图为 Markdown 文本在 API 文档中的显示效果:
|
||||
它会在交互式文档中使用:
|
||||
|
||||
<img src="/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ OpenAPI 概图会自动添加标签,供 API 文档接口使用:
|
|||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
|
||||
/// info | 说明
|
||||
/// info | 信息
|
||||
|
||||
注意,`response_description` 只用于描述响应,`description` 一般则用于描述*路径操作*。
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ OpenAPI 规定每个*路径操作*都要有响应描述。
|
|||
|
||||
## 弃用*路径操作* { #deprecate-a-path-operation }
|
||||
|
||||
`deprecated` 参数可以把*路径操作*标记为<abbr title="过时,建议不要使用">弃用</abbr>,无需直接删除:
|
||||
如果需要把*路径操作*标记为<dfn title="过时,建议不要使用">弃用</dfn>,但不删除它,可以传入 `deprecated` 参数:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
|
||||
|
||||
API 文档会把该路径操作标记为弃用:
|
||||
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ FastAPI 在 0.95.0 版本添加了对 `Annotated` 的支持(并开始推荐使
|
|||
|
||||
因此,你可以将函数声明为:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
但请记住,如果你使用 `Annotated`,你就不会遇到这个问题,因为你没有使用 `Query()` 或 `Path()` 作为函数参数的默认值。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## 按需对参数排序的技巧 { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
|
|
@ -83,13 +83,13 @@ FastAPI 在 0.95.0 版本添加了对 `Annotated` 的支持(并开始推荐使
|
|||
|
||||
Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数都应该作为关键字参数(键值对)来调用,也被称为 <abbr title="来自:K-ey W-ord Arg-uments"><code>kwargs</code></abbr>。即使它们没有默认值。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
### 使用 `Annotated` 更好 { #better-with-annotated }
|
||||
|
||||
请记住,如果你使用 `Annotated`,因为你没有使用函数参数的默认值,所以你不会有这个问题,你大概率也不需要使用 `*`。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## 数值校验:大于等于 { #number-validations-greater-than-or-equal }
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数
|
|||
|
||||
在这里,使用 `ge=1` 后,`item_id` 必须是一个整数,值要「`g`reater than or `e`qual」1。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
## 数值校验:大于和小于等于 { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数
|
|||
* `gt`:大于(`g`reater `t`han)
|
||||
* `le`:小于等于(`l`ess than or `e`qual)
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## 数值校验:浮点数、大于和小于 { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ Python 不会对这个 `*` 做任何事,但它会知道之后的所有参数
|
|||
|
||||
对于 <abbr title="less than - 小于"><code>lt</code></abbr> 也是一样的。
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
|
||||
|
||||
## 总结 { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
你可以使用与 Python 字符串格式化相同的语法声明路径“参数”或“变量”:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
|
||||
|
||||
路径参数 `item_id` 的值会作为参数 `item_id` 传递给你的函数。
|
||||
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
使用 Python 标准类型注解,声明路径操作函数中路径参数的类型:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
本例把 `item_id` 的类型声明为 `int`。
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
///
|
||||
|
||||
## 数据<abbr title="也称为:序列化、解析、编组">转换</abbr> { #data-conversion }
|
||||
## 数据<dfn title="也称为:序列化、解析、编组">转换</dfn> { #data-conversion }
|
||||
|
||||
运行示例并访问 <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>,返回的响应如下:
|
||||
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
注意,函数接收并返回的值是 `3`( `int`),不是 `"3"`(`str`)。
|
||||
|
||||
**FastAPI** 通过类型声明自动<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">**解析**请求中的数据</abbr>。
|
||||
**FastAPI** 通过类型声明自动进行请求的<dfn title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">解析</dfn>。
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -118,19 +118,19 @@ FastAPI 充分地利用了 <a href="https://docs.pydantic.dev/" class="external-
|
|||
|
||||
由于*路径操作*是按顺序依次运行的,因此,一定要在 `/users/{user_id}` 之前声明 `/users/me` :
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
否则,`/users/{user_id}` 将匹配 `/users/me`,FastAPI 会**认为**正在接收值为 `"me"` 的 `user_id` 参数。
|
||||
|
||||
同样,你不能重复定义一个路径操作:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
由于路径首先匹配,始终会使用第一个定义的。
|
||||
|
||||
## 预设值 { #predefined-values }
|
||||
|
||||
路径操作使用 Python 的 <abbr title="Enumeration">`Enum`</abbr> 类型接收预设的路径参数。
|
||||
路径操作使用 Python 的 <abbr title="Enumeration - 枚举">`Enum`</abbr> 类型接收预设的路径参数。
|
||||
|
||||
### 创建 `Enum` 类 { #create-an-enum-class }
|
||||
|
||||
|
|
@ -140,11 +140,11 @@ FastAPI 充分地利用了 <a href="https://docs.pydantic.dev/" class="external-
|
|||
|
||||
然后,创建包含固定值的类属性,这些固定值是可用的有效值:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
**AlexNet**、**ResNet**、**LeNet** 是机器学习<abbr title="技术上来说是深度学习模型架构">模型</abbr>的名字。
|
||||
**AlexNet**、**ResNet**、**LeNet** 是机器学习<dfn title="技术上来说是深度学习模型架构">模型</dfn>的名字。
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ FastAPI 充分地利用了 <a href="https://docs.pydantic.dev/" class="external-
|
|||
|
||||
使用 Enum 类(`ModelName`)创建使用类型注解的路径参数:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### 查看文档 { #check-the-docs }
|
||||
|
||||
|
|
@ -168,13 +168,13 @@ API 文档会显示预定义路径参数的可用值:
|
|||
|
||||
可以将其与枚举类 `ModelName` 中的枚举成员进行比较:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### 获取枚举值 { #get-the-enumeration-value }
|
||||
|
||||
使用 `model_name.value` 或通用的 `your_enum_member.value` 获取实际的值(本例中为 `str`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ API 文档会显示预定义路径参数的可用值:
|
|||
|
||||
返回给客户端之前,会把枚举成员转换为对应的值(本例中为字符串):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
|
||||
客户端中的 JSON 响应如下:
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ OpenAPI 不支持声明包含路径的路径参数,因为这会导致测试和
|
|||
|
||||
用法如下:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ OpenAPI 不支持声明包含路径的路径参数,因为这会导致测试和
|
|||
通过简短、直观的 Python 标准类型声明,**FastAPI** 可以获得:
|
||||
|
||||
- 编辑器支持:错误检查,代码自动补全等
|
||||
- 数据 "<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">解析</abbr>"
|
||||
- 数据 "<dfn title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">解析</dfn>"
|
||||
- 数据校验
|
||||
- API 注解和自动文档
|
||||
|
||||
|
|
|
|||
|
|
@ -47,40 +47,16 @@ FastAPI 在 0.95.0 版本中添加了对 `Annotated` 的支持(并开始推荐
|
|||
|
||||
我们之前的类型标注是:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
我们要做的是用 `Annotated` 把它包起来,变成:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
这两种写法含义相同,`q` 是一个可以是 `str` 或 `None` 的参数,默认是 `None`。
|
||||
|
||||
现在进入更有趣的部分。🎉
|
||||
|
|
@ -109,7 +85,7 @@ FastAPI 现在会:
|
|||
|
||||
## 另一种(旧的)方式:把 `Query` 作为默认值 { #alternative-old-query-as-the-default-value }
|
||||
|
||||
早期版本的 FastAPI(<abbr title="早于 2023-03">0.95.0</abbr> 之前)要求你把 `Query` 作为参数的默认值,而不是放在 `Annotated` 里。你很可能会在别处看到这种写法,所以我也给你解释一下。
|
||||
早期版本的 FastAPI(<dfn title="早于 2023-03">0.95.0</dfn> 之前)要求你把 `Query` 作为参数的默认值,而不是放在 `Annotated` 里。你很可能会在别处看到这种写法,所以我也给你解释一下。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -191,7 +167,7 @@ q: str = Query(default="rick")
|
|||
|
||||
## 添加正则表达式 { #add-regular-expressions }
|
||||
|
||||
你可以定义一个参数必须匹配的 <abbr title="正则表达式(regex 或 regexp)是用于定义字符串搜索模式的字符序列。">正则表达式</abbr> `pattern`:
|
||||
你可以定义一个参数必须匹配的 <dfn title="正则表达式(regex 或 regexp)是用于定义字符串搜索模式的字符序列。">正则表达式</dfn> `pattern`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
|
|
@ -211,7 +187,7 @@ q: str = Query(default="rick")
|
|||
|
||||
假设你想要声明查询参数 `q` 的 `min_length` 为 `3`,并且默认值为 `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
@ -241,7 +217,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
|||
|
||||
因此,在使用 `Query` 的同时需要把某个值声明为必填时,只需不声明默认值:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### 必填,但可以为 `None` { #required-can-be-none }
|
||||
|
||||
|
|
@ -292,7 +268,7 @@ http://localhost:8000/items/?q=foo&q=bar
|
|||
|
||||
你还可以定义在没有给定值时的默认 `list`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
如果你访问:
|
||||
|
||||
|
|
@ -315,7 +291,7 @@ http://localhost:8000/items/
|
|||
|
||||
你也可以直接使用 `list`,而不是 `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
@ -371,7 +347,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
|||
|
||||
现在假设你不再喜欢这个参数了。
|
||||
|
||||
由于还有客户端在使用它,你不得不保留一段时间,但你希望文档清楚地将其展示为<abbr title="已过时,不推荐使用">已弃用</abbr>。
|
||||
由于还有客户端在使用它,你不得不保留一段时间,但你希望文档清楚地将其展示为<dfn title="已过时,不推荐使用">已弃用</dfn>。
|
||||
|
||||
那么将参数 `deprecated=True` 传给 `Query`:
|
||||
|
||||
|
|
@ -401,7 +377,7 @@ Pydantic 还有 <a href="https://docs.pydantic.dev/latest/concepts/validators/#f
|
|||
|
||||
///
|
||||
|
||||
例如,这个自定义校验器会检查条目 ID 是否以 `isbn-`(用于 <abbr title="ISBN 的含义是 International Standard Book Number(国际标准书号)">ISBN</abbr> 书号)或 `imdb-`(用于 <abbr title="IMDB(Internet Movie Database)是一个包含电影信息的网站">IMDB</abbr> 电影 URL 的 ID)开头:
|
||||
例如,这个自定义校验器会检查条目 ID 是否以 `isbn-`(用于 <abbr title="International Standard Book Number - 国际标准书号">ISBN</abbr> 书号)或 `imdb-`(用于 <abbr title="Internet Movie Database - 互联网电影数据库: 一个包含电影信息的网站">IMDB</abbr> 电影 URL 的 ID)开头:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
|
|
@ -435,7 +411,7 @@ Pydantic 还有 <a href="https://docs.pydantic.dev/latest/concepts/validators/#f
|
|||
|
||||
#### 一个随机条目 { #a-random-item }
|
||||
|
||||
使用 `data.items()` 我们会得到一个包含每个字典项键和值的元组的 <abbr title="可以用 for 循环迭代的对象,例如 list、set 等">可迭代对象</abbr>。
|
||||
使用 `data.items()` 我们会得到一个包含每个字典项键和值的元组的 <dfn title="可以用 for 循环迭代的对象,例如 list、set 等">可迭代对象</dfn>。
|
||||
|
||||
我们用 `list(data.items())` 把这个可迭代对象转换成一个真正的 `list`。
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
声明的参数不是路径参数时,路径操作函数会把该参数自动解释为**查询**参数。
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
|
||||
|
||||
查询字符串是键值对的集合,这些键值对位于 URL 的 `?` 之后,以 `&` 分隔。
|
||||
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
……查询参数为:
|
||||
...查询参数为:
|
||||
|
||||
* `skip`:值为 `0`
|
||||
* `limit`:值为 `10`
|
||||
|
|
@ -24,7 +24,7 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
|
|||
所有应用于路径参数的流程也适用于查询参数:
|
||||
|
||||
* (显而易见的)编辑器支持
|
||||
* 数据<abbr title="将来自 HTTP 请求的字符串转换为 Python 数据类型">**解析**</abbr>
|
||||
* 数据<dfn title="将来自 HTTP 请求的字符串转换为 Python 数据">"解析"</dfn>
|
||||
* 数据校验
|
||||
* 自动文档
|
||||
|
||||
|
|
@ -75,7 +75,6 @@ http://127.0.0.1:8000/items/?skip=20
|
|||
|
||||
参数还可以声明为 `bool` 类型,FastAPI 会自动转换参数类型:
|
||||
|
||||
|
||||
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
|
||||
|
||||
本例中,访问:
|
||||
|
|
@ -129,7 +128,7 @@ FastAPI 通过参数名进行检测:
|
|||
|
||||
如果要把查询参数设置为**必选**,就不要声明默认值:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
|
||||
|
||||
这里的查询参数 `needy` 是类型为 `str` 的必选查询参数。
|
||||
|
||||
|
|
@ -139,7 +138,7 @@ FastAPI 通过参数名进行检测:
|
|||
http://127.0.0.1:8000/items/foo-item
|
||||
```
|
||||
|
||||
……因为路径中没有必选参数 `needy`,返回的响应中会显示如下错误信息:
|
||||
...因为路径中没有必选参数 `needy`,返回的响应中会显示如下错误信息:
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
|
@ -163,7 +162,7 @@ http://127.0.0.1:8000/items/foo-item
|
|||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
```
|
||||
|
||||
……这样就正常了:
|
||||
...这样就正常了:
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ $ pip install python-multipart
|
|||
|
||||
从 `fastapi` 导入 `File` 和 `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## 定义 `File` 参数 { #define-file-parameters }
|
||||
|
||||
像为 `Body` 或 `Form` 一样创建文件参数:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ $ pip install python-multipart
|
|||
|
||||
将文件参数的类型声明为 `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
|
||||
|
||||
与 `bytes` 相比,使用 `UploadFile` 有多项优势:
|
||||
|
||||
|
|
@ -76,9 +76,9 @@ $ pip install python-multipart
|
|||
|
||||
`UploadFile` 具有以下 `async` 方法。它们都会在底层调用对应的文件方法(使用内部的 `SpooledTemporaryFile`)。
|
||||
|
||||
* `write(data)`:将 `data`(`str` 或 `bytes`)写入文件。
|
||||
* `read(size)`:读取文件中 `size`(`int`)个字节/字符。
|
||||
* `seek(offset)`:移动到文件中字节位置 `offset`(`int`)。
|
||||
* `write(data)`:将 `data` (`str` 或 `bytes`) 写入文件。
|
||||
* `read(size)`:读取文件中 `size` (`int`) 个字节/字符。
|
||||
* `seek(offset)`:移动到文件中字节位置 `offset` (`int`)。
|
||||
* 例如,`await myfile.seek(0)` 会移动到文件开头。
|
||||
* 如果你先运行过 `await myfile.read()`,然后需要再次读取内容时,这尤其有用。
|
||||
* `close()`:关闭文件。
|
||||
|
|
@ -121,7 +121,7 @@ HTML 表单(`<form></form>`)向服务器发送数据的方式通常会对数
|
|||
|
||||
但当表单包含文件时,会编码为 `multipart/form-data`。如果你使用 `File`,**FastAPI** 会知道需要从请求体的正确位置获取文件。
|
||||
|
||||
如果你想进一步了解这些编码和表单字段,请参阅 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Mozilla 开发者网络">MDN</abbr> web docs for <code>POST</code></a>。
|
||||
如果你想进一步了解这些编码和表单字段,请参阅 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Mozilla 开发者网络">MDN</abbr> 关于 <code>POST</code> 的 Web 文档</a>。
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ HTML 表单(`<form></form>`)向服务器发送数据的方式通常会对数
|
|||
|
||||
## 可选文件上传 { #optional-file-upload }
|
||||
|
||||
您可以通过使用标准类型注解并将 None 作为默认值的方式将一个文件参数设为可选:
|
||||
您可以通过使用标准类型注解并将 `None` 作为默认值的方式将一个文件参数设为可选:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ HTML 表单(`<form></form>`)向服务器发送数据的方式通常会对数
|
|||
|
||||
您也可以将 `File()` 与 `UploadFile` 一起使用,例如,设置额外的元数据:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
|
||||
|
||||
## 多文件上传 { #multiple-file-uploads }
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ FastAPI 支持同时上传多个文件。
|
|||
|
||||
要实现这一点,声明一个由 `bytes` 或 `UploadFile` 组成的列表(`List`):
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
|
||||
|
||||
接收的也是含 `bytes` 或 `UploadFile` 的列表(`list`)。
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ FastAPI 支持同时上传多个文件。
|
|||
|
||||
和之前的方式一样,你可以为 `File()` 设置额外参数,即使是 `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
|
||||
|
||||
## 小结 { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ $ pip install python-multipart
|
|||
|
||||
你只需声明一个 **Pydantic 模型**,其中包含你希望接收的**表单字段**,然后将参数声明为 `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** 将从请求中的**表单数据**中**提取**出**每个字段**的数据,并提供你定义的 Pydantic 模型。
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ $ pip install python-multipart
|
|||
|
||||
你可以使用 Pydantic 的模型配置来 `forbid` 任何 `extra` 字段:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
|
||||
|
||||
如果客户端尝试发送一些额外的数据,他们将收到**错误**响应。
|
||||
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ $ pip install python-multipart
|
|||
|
||||
## 导入 `File` 与 `Form` { #import-file-and-form }
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## 定义 `File` 与 `Form` 参数 { #define-file-and-form-parameters }
|
||||
|
||||
创建文件和表单参数的方式与 `Body` 和 `Query` 一样:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
|
||||
|
||||
文件和表单字段作为表单数据上传与接收。
|
||||
|
||||
声明文件可以使用 `bytes` 或 `UploadFile` 。
|
||||
并且你可以将部分文件声明为 `bytes`,将部分文件声明为 `UploadFile`。
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@ $ pip install python-multipart
|
|||
|
||||
从 `fastapi` 导入 `Form`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## 定义 `Form` 参数 { #define-form-parameters }
|
||||
|
||||
创建表单参数的方式与 `Body` 或 `Query` 相同:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
例如,在 OAuth2 规范的一种使用方式(称为“密码流”)中,要求将 `username` 和 `password` 作为表单字段发送。
|
||||
|
||||
<abbr title="specification - 规范">spec</abbr> 要求这些字段必须精确命名为 `username` 和 `password`,并且作为表单字段发送,而不是 JSON。
|
||||
<dfn title="规范">规范</dfn> 要求这些字段必须精确命名为 `username` 和 `password`,并且作为表单字段发送,而不是 JSON。
|
||||
|
||||
使用 `Form` 可以像使用 `Body`(以及 `Query`、`Path`、`Cookie`)一样声明相同的配置,包括校验、示例、别名(例如将 `username` 写成 `user-name`)等。
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
|||
|
||||
最常见的情况是[直接返回 Response,详见进阶文档](../advanced/response-directly.md){.internal-link target=_blank}。
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
|
||||
|
||||
这个简单场景 FastAPI 会自动处理,因为返回类型注解是 `Response`(或其子类)。
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
|||
|
||||
你也可以在类型注解中使用 `Response` 的子类:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
|
||||
|
||||
这同样可行,因为 `RedirectResponse` 是 `Response` 的子类,FastAPI 会自动处理这个简单场景。
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ FastAPI 在内部配合 Pydantic 做了多项处理,确保不会把类继承
|
|||
|
||||
但当你返回其他任意对象(如数据库对象)而它不是有效的 Pydantic 类型,并在函数中按此进行了注解时,FastAPI 会尝试基于该类型注解创建一个 Pydantic 响应模型,但会失败。
|
||||
|
||||
如果你有一个在多个类型之间的<abbr title="多个类型的联合表示“这些类型中的任意一个”">联合类型</abbr>,其中一个或多个不是有效的 Pydantic 类型,也会发生同样的情况,例如这个会失败 💥:
|
||||
如果你有一个在多个类型之间的<dfn title="多个类型的联合表示“这些类型中的任意一个”。">联合类型</dfn>,其中一个或多个不是有效的 Pydantic 类型,也会发生同样的情况,例如这个会失败 💥:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* `@app.delete()`
|
||||
* 等...
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
|
|||
|
||||
再看下之前的例子:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`201` 表示“已创建”的状态码。
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ FastAPI 可以进行识别,并生成表明无响应体的 OpenAPI 文档。
|
|||
|
||||
可以使用 `fastapi.status` 中的快捷变量。
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
|
||||
|
||||
这只是一种快捷方式,具有相同的数字代码,但它可以使用编辑器的自动补全功能:
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ OpenAPI 3.1.0(自 FastAPI 0.99.0 起使用)增加了对 `examples` 的支持
|
|||
|
||||
这样做时,这些示例会成为该请求体数据内部 JSON Schema 的一部分。
|
||||
|
||||
不过,在<abbr title="2023-08-26">撰写本文时</abbr>,用于展示文档 UI 的 Swagger UI 并不支持显示 JSON Schema 中数据的多个示例。但请继续阅读,下面有一种变通方法。
|
||||
不过,在<dfn title="2023-08-26">撰写本文时</dfn>,用于展示文档 UI 的 Swagger UI 并不支持显示 JSON Schema 中数据的多个示例。但请继续阅读,下面有一种变通方法。
|
||||
|
||||
### OpenAPI 特定的 `examples` { #openapi-specific-examples }
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
把下面的示例代码复制到 `main.py`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py *}
|
||||
|
||||
## 运行 { #run-it }
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ OAuth2 的设计目标是让后端或 API 与负责用户认证的服务器解
|
|||
|
||||
创建 `OAuth2PasswordBearer` 类实例时,需要传入 `tokenUrl` 参数。该参数包含客户端(运行在用户浏览器中的前端)用来发送 `username` 和 `password` 以获取令牌的 URL。
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ oauth2_scheme(some, parameters)
|
|||
|
||||
现在你可以通过 `Depends` 将 `oauth2_scheme` 作为依赖传入。
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
该依赖会提供一个 `str`,赋值给*路径操作函数*的参数 `token`。
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
上一章中,(基于依赖注入系统的)安全系统向*路径操作函数*传递了 `str` 类型的 `token`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
但这并不实用。
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
但首先,让我们来看一些小的概念。
|
||||
|
||||
## 没有时间? { #in-a-hurry }
|
||||
## 赶时间 { #in-a-hurry }
|
||||
|
||||
如果你不关心这些术语,而只需要*立即*通过基于用户名和密码的身份认证来增加安全性,请跳转到接下来的章节。
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,11 @@ pwdlib 也支持 bcrypt 哈希算法,但不包含遗留算法——如果需
|
|||
|
||||
再创建一个工具函数来进行身份验证并返回用户。
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
|
||||
|
||||
当使用一个在数据库中不存在的用户名调用 `authenticate_user` 时,我们仍然会针对一个虚拟哈希运行 `verify_password`。
|
||||
|
||||
这可以确保无论用户名是否有效,端点的响应时间大致相同,从而防止可用于枚举已存在用户名的“时间攻击”(timing attacks)。
|
||||
|
||||
/// note | 注意
|
||||
|
||||
|
|
@ -152,7 +156,7 @@ $ openssl rand -hex 32
|
|||
|
||||
创建一个生成新访问令牌的工具函数。
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
|
||||
|
||||
## 更新依赖项 { #update-the-dependencies }
|
||||
|
||||
|
|
@ -162,7 +166,7 @@ $ openssl rand -hex 32
|
|||
|
||||
如果令牌无效,立即返回一个 HTTP 错误。
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
|
||||
|
||||
## 更新 `/token` 路径操作 { #update-the-token-path-operation }
|
||||
|
||||
|
|
@ -170,7 +174,7 @@ $ openssl rand -hex 32
|
|||
|
||||
创建一个真正的 JWT 访问令牌并返回它。
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
|
||||
|
||||
### 关于 JWT “主题” `sub` 的技术细节 { #technical-details-about-the-jwt-subject-sub }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
首先,使用 **FastAPI** 安全工具获取 `username` 和 `password`。
|
||||
|
||||
OAuth2 规范要求使用**密码流**时,客户端或用户必须以表单数据形式发送 `username` 和 `password` 字段。
|
||||
OAuth2 规范要求使用“密码流”时,客户端或用户必须以表单数据形式发送 `username` 和 `password` 字段。
|
||||
|
||||
并且,这两个字段必须命名为 `username` 和 `password` ,不能使用 `user-name` 或 `email` 等其它名称。
|
||||
并且,这两个字段必须命名为 `username` 和 `password`,不能使用 `user-name` 或 `email` 等其它名称。
|
||||
|
||||
不过也不用担心,前端仍可以显示终端用户所需的名称。
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ OAuth2 中,**作用域**只是声明指定权限的字符串。
|
|||
|
||||
/// info | 信息
|
||||
|
||||
`OAuth2PasswordRequestForm` 与 `OAuth2PasswordBearer` 一样,都不是 FastAPI 的特殊类。
|
||||
`OAuth2PasswordRequestForm` 并不像 `OAuth2PasswordBearer` 那样是 **FastAPI** 的特殊类。
|
||||
|
||||
**FastAPI** 把 `OAuth2PasswordBearer` 识别为安全方案。因此,可以通过这种方式把它添加至 OpenAPI。
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ UserInDB(
|
|||
|
||||
此处返回值为 `Bearer` 的响应头 `WWW-Authenticate` 也是规范的一部分。
|
||||
|
||||
任何 401**UNAUTHORIZED**HTTP(错误)状态码都应返回 `WWW-Authenticate` 响应头。
|
||||
任何 401“UNAUTHORIZED”HTTP(错误)状态码都应返回 `WWW-Authenticate` 响应头。
|
||||
|
||||
本例中,因为使用的是 Bearer Token,该响应头的值应为 `Bearer`。
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ UserInDB(
|
|||
|
||||
### 身份验证 { #authenticate }
|
||||
|
||||
点击**Authorize**按钮。
|
||||
点击“Authorize”按钮。
|
||||
|
||||
使用以下凭证:
|
||||
|
||||
|
|
@ -286,4 +286,4 @@ UserInDB(
|
|||
|
||||
唯一欠缺的是,它仍然不是真的**安全**。
|
||||
|
||||
下一章,介绍使用密码哈希支持库与 <abbr title="JSON Web Tokens - JSON Web 令牌">JWT</abbr> 令牌实现真正的安全机制。
|
||||
下一章你将看到如何使用安全的密码哈希库和 <abbr title="JSON Web Tokens - JSON Web 令牌">JWT</abbr> 令牌。
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ $ fastapi dev main.py
|
|||
|
||||
它包含与 `HeroBase` 相同的字段,因此不会包括 `secret_name`。
|
||||
|
||||
终于,我们英雄(hero)的身份得到了保护!🥷
|
||||
终于,我们英雄的身份得到了保护!🥷
|
||||
|
||||
它还重新声明了 `id: int`。这样我们便与 API 客户端建立了一种**约定**,使他们始终可以期待 `id` 存在并且是一个整数 `int`(永远不会是 `None`)。
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
* 导入 `StaticFiles`。
|
||||
* 将一个 `StaticFiles()` 实例“挂载”(Mount)到指定路径。
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
/// note | 注意
|
||||
/// note | 技术细节
|
||||
|
||||
你也可以用 `from starlette.staticfiles import StaticFiles`。
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ $ pip install httpx
|
|||
|
||||
为你需要检查的地方用标准的Python表达式写个简单的 `assert` 语句(重申,标准的`pytest`)。
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
|
||||
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ $ pip install httpx
|
|||
在 `main.py` 文件中你有一个 **FastAPI** app:
|
||||
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/main.py *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/main.py *}
|
||||
|
||||
### 测试文件 { #testing-file }
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ $ pip install httpx
|
|||
|
||||
因为这文件在同一个包中,所以你可以通过相对导入从 `main` 模块(`main.py`)导入`app`对象:
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
|
||||
|
||||
...然后测试代码和之前一样的。
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
首先,为你的工程创建一个目录。
|
||||
|
||||
我 (指原作者 —— 译者注) 通常会在我的主目录下创建一个名为 `code` 的目录。
|
||||
我通常会在我的主目录下创建一个名为 `code` 的目录。
|
||||
|
||||
在这个目录下,我再为每个工程创建一个目录。
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ $ cd awesome-project
|
|||
|
||||
## 创建一个虚拟环境 { #create-a-virtual-environment }
|
||||
|
||||
在开始一个 Python 工程的**第一时间**,**<abbr title="还有其他做法,此处仅作一个简单的指南">在你的工程内部</abbr>**创建一个虚拟环境。
|
||||
在开始一个 Python 工程的**第一时间**,**<dfn title="还有其他做法,此处仅作一个简单的指南">在你的工程内部</dfn>**创建一个虚拟环境。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -316,7 +316,7 @@ $ echo "*" > .venv/.gitignore
|
|||
|
||||
### 直接安装包 { #install-packages-directly }
|
||||
|
||||
如果你急于安装,不想使用文件来声明工程的软件包依赖,您可以直接安装它们。
|
||||
如果你急于安装,不想使用文件来声明工程的软件包依赖,你可以直接安装它们。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ Hello World
|
|||
|
||||
## 配置编辑器 { #configure-your-editor }
|
||||
|
||||
你可能会用到编辑器(即 IDE —— 译者注),请确保配置它使用与你创建的相同的虚拟环境(它可能会自动检测到),以便你可以获得自动补全和内联错误提示。
|
||||
你可能会用到编辑器,请确保配置它使用与你创建的相同的虚拟环境(它可能会自动检测到),以便你可以获得自动补全和内联错误提示。
|
||||
|
||||
例如:
|
||||
|
||||
|
|
@ -437,7 +437,7 @@ $ deactivate
|
|||
|
||||
</div>
|
||||
|
||||
这样,当你运行 `python` 时,它不会尝试从安装了软件包的虚拟环境中运行。(即,它将不再会尝试从虚拟环境中运行,也不会使用其中安装的软件包。—— 译者注)
|
||||
这样,当你运行 `python` 时,它不会尝试从那个虚拟环境及其已安装的软件包中运行。
|
||||
|
||||
## 开始工作 { #ready-to-work }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue