FastAPI 是一个现代、快速(高性能)的 Web 框架,用于基于标准 Python 类型提示,使用 Python 3.6+ 构建 API。
——Sebastián Ramírez,FastAPI 创建者
预览
FastAPI 由 Sebastián Ramírez 于 2018 年发布。从许多意义上说,它都比大多数 Python Web 框架更现代——它利用了过去几年中添加到 Python 3 的特性。本章是对 FastAPI 主要特性的快速概览,重点放在你最先想了解的内容上:如何处理 Web 请求和响应。
什么是 FastAPI?
和任何 Web 框架一样,FastAPI 帮助你构建 Web 应用程序。每个框架都是为了让某些操作变得更容易而设计的——通过它提供的功能、省略的内容以及默认设置来实现。顾名思义,FastAPI 的目标是开发 Web API,不过你也可以用它来构建传统的 Web 内容应用程序。
FastAPI 网站声称它具备以下优势:
性能
在某些情况下与 Node.js 和 Go 一样快,这对 Python 框架来说并不常见。
更快的开发速度
没有尖锐边角或奇怪之处。
更好的代码质量
类型提示和模型有助于减少 bug。
自动生成文档和测试页面
比手动编辑 OpenAPI 描述要容易得多。
FastAPI 使用以下内容:
Python 类型提示
Starlette,用于 Web 机制,包括异步支持
Pydantic,用于数据定义和验证
特殊集成,用于利用并扩展上述工具
这种组合为 Web 应用程序提供了令人愉快的开发环境,尤其适合 RESTful Web 服务。
一个 FastAPI 应用程序
让我们写一个非常小的 FastAPI 应用程序——一个只有单个端点的 Web 服务。现在,我们处在我所称的 Web 层,只处理 Web 请求和响应。首先,安装我们将使用的基础 Python 包:
FastAPI 框架:pip install fastapi
Uvicorn Web 服务器:pip install uvicorn
HTTPie 文本 Web 客户端:pip install httpie
Requests 同步 Web 客户端包:pip install requests
HTTPX 同步/异步 Web 客户端包:pip install httpx
虽然 curl 是最知名的文本 Web 客户端,但我认为 HTTPie 更容易使用。另外,它默认进行 JSON 编码和解码,这与 FastAPI 更匹配。在本章后面,你会看到一张截图,其中包含访问某个特定端点所需的 curl 命令行语法。
让我们在示例 3-1 中跟随一位内向的 Web 开发者,并把这段代码保存为 hello.py 文件。
示例 3-1 一个害羞的端点(hello.py)
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet():
return "Hello? World?"
这里有几点需要注意:
app 是顶层 FastAPI 对象,代表整个 Web 应用程序。
@app.get("/hi") 是一个路径装饰器。它告诉 FastAPI:
对这个服务器上 URL "/hi" 的请求,应该被导向下面这个函数。
这个装饰器只适用于 HTTP 的 GET 动词。你也可以响应使用其他 HTTP 动词发送到 "/hi" 这个 URL 的请求,比如 PUT、POST 等等,每种动词对应一个单独的函数。
def greet() 是一个路径函数——它是与 HTTP 请求和响应接触的主要位置。在这个例子中,它没有参数,但后面的几节会展示 FastAPI 内部还有更多内容。
下一步是在一个 Web 服务器中运行这个 Web 应用程序。FastAPI 本身并不包含 Web 服务器,但推荐使用 Uvicorn。你可以用两种方式启动 Uvicorn 和 FastAPI Web 应用程序:外部启动或内部启动。
要通过命令行从外部启动 Uvicorn,请看示例 3-2。
示例 3-2 使用命令行启动 Uvicorn
$ uvicorn hello:app --reload
其中,hello 指的是 hello.py 文件,而 app 是这个文件中的 FastAPI 变量名。
另一种方式是,在应用程序本身内部启动 Uvicorn,如示例 3-3 所示。
示例 3-3 在内部启动 Uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet():
return "Hello? World?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True)
无论哪种方式,这里的 reload 都会告诉 Uvicorn:如果 hello.py 发生变化,就重新启动 Web 服务器。在本章中,我们会大量使用这种自动重载。
默认情况下,这两种方式都会使用你机器上的 8000 端口,也就是名为 localhost 的本机地址。如果你想使用其他设置,外部和内部两种方法都支持 host 和 port 参数。
现在,服务器有了一个单独的端点 /hi,已经准备好接收请求了。
让我们用多个 Web 客户端来测试:
对于浏览器,在顶部地址栏中输入 URL。
对于 HTTPie,输入所示命令,其中 $ 代表你的系统 shell 的任何命令提示符。
对于 Requests 或 HTTPX,使用交互模式下的 Python,并在 >>> 提示符后输入内容。
正如前言中提到的,你输入的内容会使用粗体等宽字体,输出则使用普通等宽字体。
示例 3-4 到示例 3-7 展示了测试 Web 服务器全新 /hi 端点的不同方法。
示例 3-4 在浏览器中测试 /hi
http://localhost:8000/hi
示例 3-5 使用 Requests 测试 /hi
>>> import requests
>>> r = requests.get("http://localhost:8000/hi")
>>> r.json()
'Hello? World?'
示例 3-6 使用 HTTPX 测试 /hi,它与 Requests 几乎相同
>>> import httpx
>>> r = httpx.get("http://localhost:8000/hi")
>>> r.json()
'Hello? World?'
注意
使用 Requests 还是 HTTPX 来测试 FastAPI 路由都无关紧要。但第 13 章会展示在发起其他异步调用时,HTTPX 很有用的一些场景。因此,本章其余示例会使用 Requests。
示例 3-7 使用 HTTPie 测试 /hi
$ http localhost:8000/hi
HTTP/1.1 200 OK
content-length: 15
content-type: application/json
date: Thu, 30 Jun 2022 07:38:27 GMT
server: uvicorn
"Hello? World?"
在示例 3-8 中使用 -b 参数,可以跳过响应头,只打印响应体。
示例 3-8 使用 HTTPie 测试 /hi,只打印响应体
$ http -b localhost:8000/hi
"Hello? World?"
示例 3-9 使用 -v 获取完整请求头以及响应。
示例 3-9 使用 HTTPie 测试 /hi 并获取所有内容
$ http -v localhost:8000/hi
GET /hi HTTP/1.1
Accept: /
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/3.2.1
HTTP/1.1 200 OK
content-length: 15
content-type: application/json
date: Thu, 30 Jun 2022 08:05:06 GMT
server: uvicorn
"Hello? World?"
本书中的一些示例会展示 HTTPie 的默认输出,也就是响应头和响应体;另一些示例则只展示响应体。
HTTP 请求
示例 3-9 只包含一个特定请求:对服务器 localhost、端口 8000 上 /hi URL 的一个 GET 请求。
Web 请求会把数据“藏”在 HTTP 请求的不同部分中,而 FastAPI 能让你顺畅地访问它们。基于示例 3-9 中的示例请求,示例 3-10 展示了 http 命令发送给 Web 服务器的 HTTP 请求。
示例 3-10 一个 HTTP 请求
GET /hi HTTP/1.1
Accept: /
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/3.2.1
这个请求包含以下内容:
动词,也就是 GET,以及路径 /hi
任何查询参数,也就是 ? 之后的文本;在这个例子中没有
其他 HTTP 头
没有请求体内容
FastAPI 会把这些内容“解藏”成方便使用的定义:
Header
HTTP 头
Path
URL
Query
查询参数,也就是 URL 末尾 ? 之后的内容
Body
HTTP 请求体
注意
FastAPI 从 HTTP 请求的不同部分提供数据的方式,是它最好的特性之一,也改进了大多数 Python Web 框架的做法。你需要的所有参数都可以直接在路径函数中声明并提供,使用前面列表中的定义,比如 Path、Query 等,以及你自己编写的函数。这使用了一种叫作依赖注入的技术,我们会随着内容推进进行讨论,并在第 6 章中进一步展开。
让我们给前面的应用程序增加一点个性:添加一个名为 who 的参数,把那个带点哀怨的 Hello? 对某个人说出来。我们会尝试用不同方式传递这个新参数:
在 URL 路径中
作为查询参数,位于 URL 中的 ? 之后
在 HTTP 请求体中
作为 HTTP 头
URL 路径
编辑示例 3-11 中的 hello.py。
示例 3-11 返回路径中的问候语
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi/{who}")
def greet(who):
return f"Hello? {who}?"
一旦你在编辑器中保存这个修改,Uvicorn 应该会重新启动。否则,我们就得创建 hello2.py 等文件,并每次重新运行 Uvicorn。如果你有拼写错误,继续尝试直到修好即可,Uvicorn 不会为难你。
在 URL 中添加 {who},也就是在 @app.get 后面,告诉 FastAPI:在 URL 的这个位置期待一个名为 who 的变量。然后,FastAPI 会把它赋给后面 greet() 函数中的 who 参数。这展示了路径装饰器和路径函数之间的配合。
注意
不要在这里为修改后的 URL 字符串 "/hi/{who}" 使用 Python f-string。这里的花括号由 FastAPI 自身使用,用来把 URL 片段匹配为路径参数。
在示例 3-12 到 3-14 中,用前面讨论过的各种方法测试这个修改后的端点。
示例 3-12 在浏览器中测试 /hi/Mom
localhost:8000/hi/Mom
示例 3-13 使用 HTTPie 测试 /hi/Mom
$ http localhost:8000/hi/Mom
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 30 Jun 2022 08:09:02 GMT
server: uvicorn
"Hello? Mom?"
示例 3-14 使用 Requests 测试 /hi/Mom
>>> import requests
>>> r = requests.get("http://localhost:8000/hi/Mom")
>>> r.json()
'Hello? Mom?'
在每种情况下,字符串 "Mom" 都作为 URL 的一部分传入,作为 who 变量传递给 greet() 路径函数,并作为响应的一部分返回。
每种情况下的响应都是 JSON 字符串 "Hello? Mom?",具体显示单引号还是双引号,取决于你使用的是哪个测试客户端。
查询参数
查询参数是 URL 中 ? 之后的 name=value 字符串,多个参数之间用 & 字符分隔。再次编辑示例 3-15 中的 hello.py。
示例 3-15 返回查询参数中的问候语
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet(who):
return f"Hello? {who}?"
端点函数再次被定义为 greet(who),但这一次,前面装饰器那一行的 URL 中没有 {who},所以 FastAPI 现在会假定 who 是一个查询参数。使用示例 3-16 和 3-17 测试。
示例 3-16 使用浏览器测试示例 3-15
localhost:8000/hi?who=Mom
示例 3-17 使用 HTTPie 测试示例 3-15
$ http -b localhost:8000/hi?who=Mom
"Hello? Mom?"
在示例 3-18 中,你可以使用查询参数参数来调用 HTTPie,注意这里是 ==。
示例 3-18 使用 HTTPie 和 params 测试示例 3-15
$ http -b localhost:8000/hi who==Mom
"Hello? Mom?"
HTTPie 可以有多个这样的参数,而且把它们作为用空格分隔的参数来输入会更容易。
示例 3-19 和 3-20 展示了 Requests 的相同替代方式。
示例 3-19 使用 Requests 测试示例 3-15
>>> import requests
>>> r = requests.get("http://localhost:8000/hi?who=Mom")
>>> r.json()
'Hello? Mom?'
示例 3-20 使用 Requests 和 params 测试示例 3-15
>>> import requests
>>> params = {"who": "Mom"}
>>> r = requests.get("http://localhost:8000/hi", params=params)
>>> r.json()
'Hello? Mom?'
在每种情况下,你都以一种新方式提供了 "Mom" 这个字符串,并让它到达路径函数,最终进入响应。
请求体
我们可以向 GET 端点提供路径参数或查询参数,但不能从请求体中提供值。在 HTTP 中,GET 应该是幂等的——这是一个计算机术语,意思是问同样的问题,得到同样的答案。HTTP GET 应该只返回东西。请求体用于在创建,也就是 POST,或更新,也就是 PUT 或 PATCH 时,向服务器发送东西。第 9 章会展示一种绕过这个限制的方法。
所以,在示例 3-21 中,让我们把端点从 GET 改为 POST。从技术上说,我们并没有创建任何东西,因此使用 POST 并不完全合规;但如果 RESTful 霸主们起诉我们,那就,嘿,顺便看看那座酷炫的法院吧。
示例 3-21 返回请求体中的问候语
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/hi")
def greet(who:str = Body(embed=True)):
return f"Hello? {who}?"
注意
这里需要 Body(embed=True),用来告诉 FastAPI:这一次,我们从 JSON 格式的请求体中获取 who 的值。embed 部分意味着它应该看起来像 {"who": "Mom"},而不只是 "Mom"。
在示例 3-22 中使用 HTTPie 进行测试,使用 -v 展示生成的请求体,并注意这里用单个 = 参数来表示 JSON 请求体数据。
示例 3-22 使用 HTTPie 测试示例 3-21
$ http -v localhost:8000/hi who=Mom
POST /hi HTTP/1.1
Accept: application/json, /;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 14
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/3.2.1
{
"who": "Mom"
}
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 30 Jun 2022 08:37:00 GMT
server: uvicorn
"Hello? Mom?"
最后,在示例 3-23 中使用 Requests 测试,它使用 json 参数在请求体中传递 JSON 编码数据。
示例 3-23 使用 Requests 测试示例 3-21
>>> import requests
>>> r = requests.post("http://localhost:8000/hi", json={"who": "Mom"})
>>> r.json()
'Hello? Mom?'
HTTP 头
最后,让我们在示例 3-24 中尝试把问候参数作为 HTTP 头传递。
示例 3-24 返回 HTTP 头中的问候语
from fastapi import FastAPI, Header
app = FastAPI()
@app.post("/hi")
def greet(who:str = Header()):
return f"Hello? {who}?"
在示例 3-25 中,我们只用 HTTPie 测试这个版本。它使用 name:value 来指定一个 HTTP 头。
示例 3-25 使用 HTTPie 测试示例 3-24
$ http -v localhost:8000/hi who:Mom
GET /hi HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/3.2.1
who: Mom
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Mon, 16 Jan 2023 05:14:46 GMT
server: uvicorn
"Hello? Mom?"
FastAPI 会把 HTTP 头键转换为小写,并把连字符 - 转换为下划线 _。因此,你可以像示例 3-26 和 3-27 这样打印 HTTP User-Agent 头的值。
示例 3-26 返回 User-Agent 头(hello.py)
from fastapi import FastAPI, Header
app = FastAPI()
@app.post("/agent")
def get_agent(user_agent:str = Header()):
return user_agent
示例 3-27 使用 HTTPie 测试 User-Agent 头
$ http -v localhost:8000/agent
GET /agent HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/3.2.1
HTTP/1.1 200 OK
content-length: 14
content-type: application/json
date: Mon, 16 Jan 2023 05:21:35 GMT
server: uvicorn
"HTTPie/3.2.1"
多种请求数据
你可以在同一个路径函数中使用以上多种方法。也就是说,你可以从 URL、查询参数、HTTP 请求体、HTTP 头、cookie 等位置获取数据。你还可以编写自己的依赖函数,以特殊方式处理并组合它们,例如用于分页或认证。你会在第 6 章以及第 III 部分的各章中看到其中一些用法。
哪种方法最好?
这里有几个建议:
在 URL 中传递参数时,遵循 RESTful 指南是标准实践。
查询字符串通常用于提供可选参数,比如分页。
请求体通常用于更大的输入,比如完整或部分模型。
在每种情况下,如果你在数据定义中提供类型提示,你的参数都会由 Pydantic 自动进行类型检查。这确保它们既存在,也正确。
HTTP 响应
默认情况下,FastAPI 会把你从端点函数返回的任何内容转换为 JSON;HTTP 响应中会有一行头:Content-type: application/json。因此,虽然 greet() 函数最初返回字符串 "Hello? World?",FastAPI 会把它转换为 JSON。这是 FastAPI 为简化 API 开发而选择的默认设置之一。
在这个例子中,Python 字符串 "Hello? World?" 会被转换为与之等价的 JSON 字符串 "Hello? World?",也就是同一个该死的字符串。但你返回的任何东西都会被转换为 JSON,无论是 Python 内置类型,还是 Pydantic 模型。
状态码
默认情况下,FastAPI 返回 200 状态码;异常会引发 4xx 状态码。
在路径装饰器中,可以指定一切顺利时应该返回的 HTTP 状态码;异常会生成自己的状态码并覆盖它。把示例 3-28 中的代码添加到你的 hello.py 中某个位置即可,避免一次又一次展示整个文件,然后用示例 3-29 进行测试。
示例 3-28 指定 HTTP 状态码(添加到 hello.py)
@app.get("/happy")
def happy(status_code=200):
return ":)"
示例 3-29 测试 HTTP 状态码
$ http localhost:8000/happy
HTTP/1.1 200 OK
content-length: 4
content-type: application/json
date: Sun, 05 Feb 2023 04:37:32 GMT
server: uvicorn
":)"
响应头
你可以注入 HTTP 响应头,如示例 3-30 所示。你不需要返回 response。
示例 3-30 设置 HTTP 头(添加到 hello.py)
from fastapi import Response
@app.get("/header/{name}/{value}")
def header(name: str, value: str, response:Response):
response.headers[name] = value
return "normal body"
让我们看看它是否生效了,见示例 3-31。
示例 3-31 测试响应 HTTP 头
$ http localhost:8000/header/marco/polo
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Wed, 31 May 2023 17:47:38 GMT
marco: polo
server: uvicorn
"normal body"
响应类型
响应类型包括以下几种,可以从 fastapi.responses 导入这些类:
JSONResponse,默认值
HTMLResponse
PlainTextResponse
RedirectResponse
FileResponse
StreamingResponse
我会在第 15 章中进一步说明最后两个。
对于其他输出格式,也称为 MIME 类型,你可以使用通用的 Response 类,它需要以下内容:
content
字符串或字节
media_type
字符串形式的 MIME 类型
status_code
HTTP 整数状态码
headers
字符串字典
类型转换
路径函数可以返回任何内容。默认情况下,也就是使用 JSONResponse 时,FastAPI 会把它转换成 JSON 字符串并返回,同时带上匹配的 HTTP 响应头 Content-Length 和 Content-Type。这包括任何 Pydantic 模型类。
但它是怎么做到的?如果你用过 Python 的 json 库,可能见过它在遇到某些数据类型时会抛出异常,比如 datetime。FastAPI 使用一个名为 jsonable_encoder() 的内部函数,把任何数据结构转换为一种“可 JSON 化”的 Python 数据结构,然后再调用通常的 json.dumps(),把它变成 JSON 字符串。示例 3-32 展示了一个可以用 pytest 运行的测试。
示例 3-32 使用 jsonable_encoder() 避免 JSON 爆炸
import datetime
import pytest
from fastapi.encoders import jsonable_encoder
import json
@pytest.fixture
def data():
return datetime.datetime.now()
def test_json_dump(data):
with pytest.raises(Exception):
_ = json.dumps(data)
def test_encoder(data):
out = jsonable_encoder(data)
assert out
json_out = json.dumps(out)
assert json_out
模型类型和 response_model
可以有不同的类,它们拥有许多相同字段,但一个专门用于用户输入,一个用于输出,一个用于内部使用。这些变体存在的一些原因可能包括:
从输出中移除某些敏感信息,比如如果你遇到过《健康保险流通与责任法案》(HIPAA)要求,那么就需要对个人医疗数据去标识化。
向用户输入中添加字段,比如创建日期和时间。
示例 3-33 展示了一个人为构造案例中的三个相关类:
TagIn 是定义用户需要提供内容的类;在这个例子中,只是一个名为 tag 的字符串。
Tag 由 TagIn 生成,并添加两个字段:created,表示这个 Tag 何时被创建;secret,一个内部字符串,可能存储在数据库中,但绝不应该暴露给外部世界。
TagOut 是定义可以返回给用户内容的类,比如由查找或搜索端点返回。它包含原始 TagIn 对象和派生出的 Tag 对象中的 tag 字段,以及为 Tag 生成的 created 字段,但不包含 secret。
示例 3-33 模型变体(model/tag.py)
from datetime import datetime
from pydantic import BaseClass
class TagIn(BaseClass):
tag: str
class Tag(BaseClass):
tag: str
created: datetime
secret: str
class TagOut(BaseClass):
tag: str
created: datetime
你可以通过不同方式,从 FastAPI 路径函数返回默认 JSON 之外的数据类型。其中一种方法是在路径装饰器中使用 response_model 参数,促使 FastAPI 返回其他东西。FastAPI 会丢弃你返回的对象中存在、但 response_model 指定对象中不存在的任何字段。
在示例 3-34 中,假装你写了一个新的服务模块 service/tag.py,其中包含 create() 和 get() 函数,供这个 Web 模块调用。那些更低栈层的细节在这里并不重要。重点是底部的 get_one() 路径函数,以及它的路径装饰器中的 response_model=TagOut。这会自动把一个内部 Tag 对象转换为经过清理的 TagOut 对象。
示例 3-34 使用 response_model 返回不同的响应类型(web/tag.py)
import datetime
from model.tag import TagIn, Tag, TagOut
import service.tag as service
@app.post('/')
def create(tag_in: TagIn) -> TagIn:
tag: Tag = Tag(tag=tag_in.tag, created=datetime.utcnow(),
secret="shhhh")
service.create(tag)
return tag_in
@app.get('/{tag_str}', response_model=TagOut)
def get_one(tag_str: str) -> TagOut:
tag: Tag = service.get(tag_str)
return tag
即使我们返回的是一个 Tag,response_model 也会把它转换为 TagOut。
自动化文档
本节假设你正在运行示例 3-21 中的 Web 应用程序,也就是通过 POST 请求向 http://localhost:8000/hi 的 HTTP 请求体中发送 who 参数的那个版本。
说服你的浏览器访问这个 URL:
http://localhost:8000/docs
你会看到类似图 3-1 开头的内容。下面的截图我做了裁剪,以便突出特定区域。
图 3-1 生成的文档页面
这是从哪里来的?
FastAPI 会根据你的代码生成 OpenAPI 规范,并包含这个页面,用于展示和测试你的所有端点。这只是它秘制酱汁中的一种配料。
点击绿色框右侧的向下箭头,打开它进行测试,见图 3-2。
图 3-2 打开的文档页面
点击右侧的 “Try it out” 按钮。现在你会看到一个区域,可以在 body 部分输入一个值,见图 3-3。
图 3-3 数据输入页面
点击那个 "string"。把它改成 "Cousin Eddie",保留外面的双引号。然后点击底部蓝色的 Execute 按钮。
现在查看 Execute 按钮下方的 Responses 区域,见图 3-4。
“Response body” 框显示 Cousin Eddie 出现了。
所以,这又是测试网站的另一种方式,除了前面使用浏览器、HTTPie 和 Requests 的示例之外。
图 3-4 响应页面
顺便说一句,正如你可以在 Responses 显示中的 Curl 框看到的,如果使用 curl 而不是 HTTPie 进行命令行测试,会需要输入更多内容。HTTPie 的自动 JSON 编码在这里很有帮助。
提示
这种自动化文档实际上是一件非常、非常重要的事。随着你的 Web 服务增长到数百个端点,一个始终保持最新的文档和测试页面会非常有帮助。
复杂数据
这些示例只展示了如何向端点传递一个单独的字符串。许多端点,尤其是 GET 或 DELETE 端点,可能完全不需要参数,或者只需要几个简单参数,比如字符串和数字。但在创建,也就是 POST,或修改,也就是 PUT 或 PATCH 某个资源时,我们通常需要更复杂的数据结构。第 5 章会展示 FastAPI 如何使用 Pydantic 和数据模型,干净地实现这些内容。
回顾
在本章中,我们使用 FastAPI 创建了一个只有单个端点的网站。多个 Web 客户端对它进行了测试:Web 浏览器、HTTPie 文本程序、Requests Python 包,以及 HTTPX Python 包。从一个简单的 GET 调用开始,请求参数通过 URL 路径、查询参数和 HTTP 头传递给服务器。之后,HTTP 请求体被用来向一个 POST 端点发送数据。随后,本章展示了如何返回各种 HTTP 响应类型。最后,一个自动生成的表单页面同时提供了文档和实时表单,作为第四种测试客户端。
这个 FastAPI 概览将在第 8 章中进一步展开。