简单易懂的 FastAPI 教程:新手友好
欢迎来到 FastAPI 的世界!如果你是一个想要学习如何构建现代、高效、易于维护的 Web API 的 Python 开发者,那么你来对地方了。FastAPI 是一个相对年轻但发展迅猛的 Python Web 框架,它以其高性能、易学易用、自动生成文档等特性赢得了开发者们的喜爱。
这篇教程将带你从零开始,一步步了解 FastAPI 的核心概念和用法。我们将专注于基础知识,确保即使你之前没有接触过任何 Web 框架,也能轻松跟上。
预计阅读时长:约 30 分钟(加上实践操作)
为什么选择 FastAPI?
在深入学习之前,我们先花点时间了解一下为什么 FastAPI 如此受欢迎。Python 社区已经有很多优秀的 Web 框架,比如 Django 和 Flask。FastAPI 有什么特别之处呢?
- 高性能:FastAPI 基于 Starlette (一个轻量级的 ASGI 框架) 和 Pydantic (用于数据验证和序列化),利用了 Python 异步编程的优势。在异步支持下,FastAPI 在处理大量并发请求时表现出色,性能可以与 Node.js 和 Go 等语言构建的 API 相媲美。
- 开发效率高:得益于 Python 的类型提示 (Type Hinting) 和 Pydantic 的强大功能,FastAPI 可以自动验证请求数据、序列化响应数据,并自动生成交互式 API 文档。这大大减少了手动编写验证代码和文档的工作量,让你更专注于业务逻辑。
- 易学易用:FastAPI 的设计哲学是简单直观。它的 API 设计清晰,很多概念可以通过函数参数的类型提示来表达,非常符合 Python 开发者的习惯。
- 自动文档:这是 FastAPI 的一大亮点。开箱即用,FastAPI 会根据你的代码(特别是类型提示和 Pydantic 模型)自动生成符合 OpenAPI 标准的 API 文档,包括 Swagger UI 和 ReDoc 两种风格。这些文档不仅美观,而且具有交互性,可以直接在浏览器中测试你的 API。
- 强大的类型提示支持:FastAPI 深度利用了 Python 的类型提示。这不仅帮助你在开发阶段捕获错误(静态分析工具如 MyPy),也让 FastAPI 能够理解你的数据结构,从而实现自动验证、序列化和文档生成。
- 现代 Python 特性:FastAPI 充分利用了 Python 3.7+ 的新特性,特别是
async
/await
,使得编写异步代码更加简洁高效。
总而言之,如果你想构建一个快速、可靠、易于维护且自带漂亮文档的 API,FastAPI 是一个非常棒的选择。
准备工作
在开始之前,你需要确保你的计算机上安装了 Python 3.7 或更高版本。FastAPI 依赖于一些 Python 3.7+ 的特性。
你可以打开终端或命令行窗口,输入以下命令检查 Python 版本:
“`bash
python –version
或者
python3 –version
“`
如果版本低于 3.7,你需要先升级你的 Python 环境。
此外,你需要一个包管理器来安装 FastAPI 和相关的库。Python 自带了 pip
,我们将使用它。
安装 FastAPI 和 Uvicorn
FastAPI 本身是一个框架,它需要一个 ASGI 服务器来运行。常用的 ASGI 服务器包括 Uvicorn、Hypercorn 等。对于大多数情况,Uvicorn 是一个很好的选择,它高性能且易于使用。
我们将安装 FastAPI 和 Uvicorn。打开你的终端或命令行,执行以下命令:
bash
pip install fastapi uvicorn[standard]
pip install fastapi
:安装 FastAPI 框架本身。pip install uvicorn[standard]
:安装 Uvicorn 服务器。[standard]
是一个可选的依赖集,它会安装一些常用的额外依赖,比如python-dotenv
(用于加载环境变量)和watchgod
或python-multipart
(用于文件上传,但在本文基础教程中用不到,不过安装了也没坏处)。对于新手来说,直接安装uvicorn[standard]
是一个方便的选择。如果你只需要最基本的 Uvicorn,可以只安装pip install uvicorn
。
安装完成后,你就可以开始编写你的第一个 FastAPI 应用了!
你的第一个 FastAPI 应用:”Hello World”
创建一个新的 Python 文件,比如命名为 main.py
。然后将以下代码复制到文件中:
“`python
main.py
从 fastapi 库中导入 FastAPI 类
from fastapi import FastAPI
创建一个 FastAPI 应用实例
这个实例是你的 API 的核心,用来定义路由、处理请求等
app = FastAPI()
使用装饰器 @app.get() 定义一个 GET 请求的路由
“@app.get(“/”)” 表示当客户端使用 GET 方法访问应用的根路径 “/” 时,
将会执行下面紧跟着的函数 read_root
@app.get(“/”)
定义一个异步函数来处理这个请求
在现代 Python Web 开发中,使用 async/await 是处理并发请求的常用方式
FastAPI 推荐使用 async def,当然你也可以使用 def 定义普通函数
async def read_root():
# 函数返回一个 Python 字典
# FastAPI 会自动将字典转换为 JSON 格式的响应
return {“Hello”: “World”}
注意:这个文件是用来定义你的 FastAPI 应用的。
你不能直接运行这个文件 (python main.py)。
你需要使用 ASGI 服务器 (如 Uvicorn) 来启动你的应用。
“`
这段代码非常简洁,但它已经是一个功能完整的 FastAPI 应用了!
让我们分解一下这段代码:
from fastapi import FastAPI
: 从fastapi
库中导入FastAPI
类。这是构建 FastAPI 应用的基础。app = FastAPI()
: 创建FastAPI
类的一个实例,并将其赋值给变量app
。这个app
实例将是你定义所有 API 接口的入口。@app.get("/")
: 这是一个 Python 装饰器。它告诉 FastAPI,紧随其后的函数(read_root
)应该处理所有针对根路径"/"
的GET
请求。FastAPI 支持所有标准的 HTTP 方法,比如@app.post()
,@app.put()
,@app.delete()
等。async def read_root():
: 定义一个异步函数。当收到匹配@app.get("/")
的请求时,FastAPI 会调用这个函数。函数的名称(read_root
)可以随意取,重要的是它紧跟在装饰器下面。async
关键字表明这是一个协程(coroutine),可以进行异步操作。虽然在这个简单的例子中没有进行异步操作,但养成使用async def
的习惯对于构建高性能应用很有帮助。对于不需要进行异步操作的函数,你也可以使用普通的def
关键字。return {"Hello": "World"}
: 函数返回一个 Python 字典。FastAPI 会自动将这个字典转换为 JSON 格式的 HTTP 响应,并发送给客户端。默认的 HTTP 状态码是 200 OK。
这就是 “Hello World” 应用的全部代码和解释。接下来,我们学习如何运行它。
运行你的 FastAPI 应用 (使用 Uvicorn)
正如代码中的注释所述,你不能直接运行 main.py
文件。你需要使用安装好的 Uvicorn 服务器来启动它。
打开你的终端或命令行,确保当前目录是你保存 main.py
文件的目录。然后运行以下命令:
bash
uvicorn main:app --reload
让我们解释一下这个命令:
uvicorn
: 启动 Uvicorn 服务器。main:app
: 告诉 Uvicorn 去哪里找到你的 FastAPI 应用实例。main
是你的 Python 文件的名称(不包含.py
扩展名),app
是你在文件中创建的 FastAPI 实例的变量名(app = FastAPI()
)。所以格式是文件名:应用实例变量名
。--reload
: 这是一个非常有用的开发选项。它会监听你文件系统的变化。当你修改并保存main.py
或其他项目文件时,Uvicorn 会自动重新加载应用,这样你就不必手动停止和启动服务器了。注意:只在开发时使用--reload
,生产环境中不要使用,因为它会增加开销。
运行命令后,你应该会看到类似以下的输出:
INFO: Will watch for changes in these directories: ['/path/to/your/project']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [xxxxx]
INFO: Started server process [xxxxx]
INFO: Waiting for application startup.
INFO: Application startup complete.
这表示你的 FastAPI 应用已经在本地启动,监听在 http://127.0.0.1:8000
地址和 8000 端口上。
测试你的第一个 API
现在服务器已经在运行了,你可以测试一下你的 API。
-
使用浏览器: 打开你的 Web 浏览器,访问
http://127.0.0.1:8000/
。你应该会看到页面上显示:json
{"Hello":"World"}浏览器默认使用 GET 方法访问 URL,所以它成功地触发了你定义的
@app.get("/")
路由。 -
使用
curl
命令: 如果你熟悉命令行工具,可以使用curl
来测试:bash
curl http://127.0.0.1:8000/你应该也会看到同样的输出:
json
{"Hello":"World"}
恭喜你!你已经成功构建并运行了你的第一个 FastAPI 应用!
理解自动生成的 API 文档
FastAPI 的一个强大特性是它自动生成交互式 API 文档。当你的应用运行时,你可以访问两个特殊的路径来查看这些文档:
- Swagger UI: 访问
http://127.0.0.1:8000/docs
- ReDoc: 访问
http://127.0.0.1:8000/redoc
打开浏览器访问 http://127.0.0.1:8000/docs
。你应该会看到一个漂亮的页面,列出了你所有的 API 接口。对于我们当前的 “Hello World” 应用,你会看到一个针对根路径 /
的 GET 请求接口。
(这是一个示例图,实际看到的会根据你的API变化)
你可以点击那个接口,展开详情,甚至点击 “Try it out” 按钮来直接在浏览器中发送请求并查看响应!
访问 http://127.0.0.1:8000/redoc
会看到另一种风格的文档,通常更适合作为静态 API 参考文档。
(这是一个示例图)
这些文档是根据你的代码(特别是我们后面会介绍的类型提示和 Pydantic 模型)自动生成的,无需你额外编写文档代码,这极大地提高了开发效率和文档的准确性。
路由与 HTTP 方法
在 “Hello World” 示例中,我们使用了 @app.get("/")
定义了一个处理 GET 请求的路由。FastAPI 支持所有标准的 HTTP 方法:
@app.get()
: 处理 GET 请求,常用于获取资源。@app.post()
: 处理 POST 请求,常用于创建新资源或提交数据。@app.put()
: 处理 PUT 请求,常用于更新资源。@app.delete()
: 处理 DELETE 请求,常用于删除资源。@app.options()
: 处理 OPTIONS 请求。@app.head()
: 处理 HEAD 请求。@app.patch()
: 处理 PATCH 请求,常用于部分更新资源。@app.trace()
: 处理 TRACE 请求。
你可以在你的 main.py
文件中添加更多的路由和方法。例如:
“`python
main.py (在之前的代码基础上添加)
… (前面的导入和 app = FastAPI() 代码不变)
再次定义根路由,仍然是 GET
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
定义一个处理 POST 请求的路由,路径也是 “/”
注意:虽然路径相同,但因为 HTTP 方法不同 (GET vs POST),它们是两个独立的路由
@app.post(“/”)
async def create_item_root():
return {“message”: “Received a POST request at root”}
定义一个处理 GET 请求的路由,路径是 “/items/”
@app.get(“/items/”)
async def read_items():
return [{“item_id”: “Foo”}, {“item_id”: “Bar”}]
定义一个处理 GET 请求的路由,路径是 “/users/”
@app.get(“/users/”)
async def read_users():
return [{“user_id”: “Alice”}, {“user_id”: “Bob”}]
注意:如果 Uvicorn 已经在运行 (–reload 模式),
你只需要保存 main.py,服务器会自动重启并加载新的路由。
“`
保存文件,访问 http://127.0.0.1:8000/docs
刷新页面,你会看到 /
路径下有两个方法 (GET, POST),还有 /items/
和 /users/
的 GET 方法。你可以尝试使用 Swagger UI 来测试 /items/
和 /users/
接口。
路径参数 (Path Parameters)
在很多 Web API 中,我们需要根据 URL 中的一部分来获取特定的资源。例如,/items/5
可能表示获取 ID 为 5 的物品。FastAPI 允许你在路径中定义参数,并使用 Python 的类型提示来自动验证和转换这些参数。
在路径中定义参数的方法是在路径字符串中使用花括号 {}
包裹参数名。然后,在对应的函数定义中,使用同名的函数参数来接收这个值,并为其添加类型提示。
修改 main.py
文件:
“`python
main.py (在之前的代码基础上添加或修改)
from fastapi import FastAPI
app = FastAPI()
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
定义一个带有路径参数的 GET 路由
路径中的 {item_id} 是路径参数的名称
函数参数 item_id: int 表示这个参数的名称是 item_id,类型是整数 (int)
@app.get(“/items/{item_id}”)
函数参数名必须和路径参数名一致 ({item_id} -> item_id)
FastAPI 会自动从路径中提取 {item_id} 的值,尝试将其转换为 int 类型,
并作为参数传递给 read_item 函数。
async def read_item(item_id: int):
# 返回包含 item_id 的字典
return {“item_id”: item_id}
注意:如果你有多个路径参数,它们都会作为函数参数传入
@app.get(“/users/{user_id}/items/{item_id}”)
async def read_user_item(user_id: int, item_id: str):
return {“user_id”: user_id, “item_id”: item_id}
路径操作顺序的重要性
当你有多个包含路径参数的路径时,它们的定义顺序很重要。
FastAPI 会按照代码中路由定义的顺序进行匹配。
如果你先定义一个更泛化的路径,再定义一个更具体的路径,泛化的路径可能会优先匹配。
示例:如果你先定义 @app.get(“/users/{user_id}”)
再定义 @app.get(“/users/me”)
当你访问 “/users/me” 时,前面的路由可能会先匹配,认为 “me” 是 user_id 的值。
解决方法:将更具体的固定路径放在前面
错误示例(如果你这样写,访问 /users/me 会匹配到 read_user):
@app.get(“/users/{user_id}”)
async def read_user(user_id: int):
return {“user_id”: user_id}
@app.get(“/users/me”) # 这个路由可能永远不会被匹配到,或者匹配行为不符合预期
async def read_user_me():
return {“current_user”: “the_logged_in_user”}
正确示例(将固定路径放在前面):
@app.get(“/users/me”) # 先匹配固定路径
async def read_user_me():
return {“current_user”: “the_logged_in_user”}
@app.get(“/users/{user_id}”) # 再匹配带参数的路径
async def read_user(user_id: int):
return {“user_id”: user_id}
“`
保存文件。Uvicorn 会自动重新加载。
现在访问 http://127.0.0.1:8000/docs
。你会看到 /items/{item_id}
接口,并且 Swagger UI 会清楚地显示 item_id
是一个路径参数,类型是整数。
尝试访问:
http://127.0.0.1:8000/items/5
: 应该返回{"item_id": 5}
。FastAPI 成功地将路径中的 “5” 转换为整数 5 并传递给函数。http://127.0.0.1:8000/items/abc
: 应该返回一个 422 Unprocessable Entity 错误响应。FastAPI 自动进行了数据验证,发现 “abc” 无法转换为整数,因此拒绝了请求并返回了标准的验证错误信息。这就是类型提示带来的强大功能之一!http://127.0.0.1:8000/users/10/items/foo
: 应该返回{"user_id": 10, "item_id": "foo"}
。http://127.0.0.1:8000/users/me
: 应该返回{"current_user": "the_logged_in_user"}
。http://127.0.0.1:8000/users/10
: 应该返回{"user_id": 10}
。
通过简单的类型提示,FastAPI 替你完成了参数提取、类型转换和数据验证的工作,是不是很方便?
查询参数 (Query Parameters)
除了路径参数,Web API 还经常使用查询参数。查询参数是 URL 中 ?
后面的键值对,用 &
分隔。例如,/items/?skip=0&limit=10
可能用于分页。
在 FastAPI 中,处理查询参数非常简单。只需要在你的函数定义中,添加不属于路径参数且没有默认值的函数参数。FastAPI 会自动识别它们是查询参数。
修改 main.py
:
“`python
main.py (在之前的代码基础上添加或修改)
from fastapi import FastAPI
from typing import Optional # 导入 Optional,用于表示参数是可选的
app = FastAPI()
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
@app.get(“/items/{item_id}”)
async def read_item(item_id: int):
return {“item_id”: item_id}
定义一个带有查询参数的 GET 路由
/items/ 路径
函数参数 skip: int = 0 表示参数名为 skip,类型是整数,默认值是 0
函数参数 limit: int = 10 表示参数名为 limit,类型是整数,默认值是 10
@app.get(“/items/”)
async def read_items(skip: int = 0, limit: int = 10):
# 在实际应用中,你可能会根据 skip 和 limit 从数据库中查询数据
return {“skip”: skip, “limit”: limit}
带有可选查询参数和默认值的例子
q: Optional[str] = None 表示参数 q 是可选的字符串类型,默认值为 None
Optional[str] 是 typing 模块提供的类型提示,等同于 str | None (在 Python 3.10+ 中)
@app.get(“/optional_params/”)
async def read_optional_params(q: Optional[str] = None, required_param: int = 100):
results = {“required_param”: required_param}
if q: # 如果 q 参数被提供了(不是 None)
results.update({“q”: q})
return results
另一个例子,结合路径参数和查询参数
@app.get(“/users/{user_id}/items/{item_id}”)
async def read_user_item(user_id: int, item_id: str, q: Optional[str] = None, short: bool = False):
item = {“item_id”: item_id, “user_id”: user_id}
if q:
item.update({“q”: q})
if not short: # 如果 short 参数为 False (默认值),则添加 description
item.update(
{“description”: “This is an amazing item that belongs to a user.”}
)
return item
“`
保存文件,Uvicorn 重新加载。访问 http://127.0.0.1:8000/docs
查看文档。你会发现 /items/
GET 接口现在显示了 skip
和 limit
两个查询参数,并显示了它们的类型和默认值。/optional_params/
和 /users/{user_id}/items/{item_id}
接口也显示了相应的查询参数及其可选性或默认值。
尝试访问:
http://127.0.0.1:8000/items/
: 返回{"skip": 0, "limit": 10}
(使用了默认值)。http://127.0.0.1:8000/items/?skip=5
: 返回{"skip": 5, "limit": 10}
(只提供了 skip)。http://127.0.0.1:8000/items/?limit=20
: 返回{"skip": 0, "limit": 20}
(只提供了 limit)。http://127.0.0.1:8000/items/?skip=5&limit=20
: 返回{"skip": 5, "limit": 20}
(提供了所有参数)。http://127.0.0.1:8000/optional_params/
: 返回{"required_param": 100}
。http://127.0.0.1:8000/optional_params/?q=somequery
: 返回{"required_param": 100, "q": "somequery"}
。http://127.0.0.1:8000/optional_params/?required_param=50&q=another
: 返回{"required_param": 50, "q": "another"}
。http://127.0.0.1:8000/users/10/items/foo?q=bar
: 返回{"item_id": "foo", "user_id": 10, "q": "bar", "description": "This is an amazing item that belongs to a user."}
。http://127.0.0.1:8000/users/10/items/foo?short=True
: 返回{"item_id": "foo", "user_id": 10}
(因为 short 是布尔类型,FastAPI 会将 “True” 字符串转换为布尔值 True)。
FastAPI 同样利用类型提示处理查询参数的验证和转换(例如将 “5” 转换为整数 5,将 “True” 转换为布尔值 True)。如果提供的参数类型不正确,FastAPI 也会返回 422 错误。
总结一下路径参数和查询参数的区别:
- 路径参数: 是 URL 路径中的一部分,用于标识资源。在函数中作为函数参数,没有默认值。
- 查询参数: 是 URL 中
?
后面的键值对,用于过滤、排序、分页等。在函数中作为函数参数,带有默认值 (包括None
)。
FastAPI 会根据函数参数是否在路径中出现、以及是否设置了默认值来智能地判断它是路径参数还是查询参数。
请求体 (Request Body)
GET 请求通常不包含请求体(Request Body),数据通过路径参数或查询参数传递。但是对于 POST、PUT、PATCH 等请求,客户端通常会在请求体中发送数据,比如创建新用户的信息、更新物品的详情等。这些数据通常是 JSON 格式。
FastAPI 处理请求体非常方便,它利用了 Pydantic 库来定义数据结构、进行数据验证和序列化。
使用 Pydantic 定义数据模型
首先,你需要安装 Pydantic (如果你之前安装 uvicorn[standard]
了,它可能已经安装了,但为了明确,你可以再运行一次):
bash
pip install pydantic
然后,使用 Pydantic 的 BaseModel
来定义你的数据模型。创建一个新的文件 models.py
(或者直接写在 main.py
中,但分开管理模型是更好的实践):
“`python
models.py
导入 BaseModel 类
from pydantic import BaseModel
from typing import Optional # 导入 Optional
定义一个 Item 数据模型,继承自 BaseModel
BaseModel 会利用类型提示自动创建字段并提供验证、序列化等功能
class Item(BaseModel):
# 定义模型的字段名和类型
name: str
# description 是可选的字符串类型,默认值是 None
description: Optional[str] = None
# price 是浮点数类型
price: float
# tax 也是可选的浮点数类型,默认值是 None
tax: Optional[float] = None
你可以根据需要定义更多模型
class User(BaseModel):
username: str
full_name: Optional[str] = None
“`
在这个 Item
模型中:
name: str
定义了一个名为name
的字段,类型是字符串。这是必填字段。description: Optional[str] = None
定义了一个名为description
的字段,类型是可选的字符串,默认值是None
。这是可选字段。price: float
定义了一个名为price
的字段,类型是浮点数。这是必填字段。tax: Optional[float] = None
定义了一个名为tax
的字段,类型是可选的浮点数,默认值是None
。这是可选字段。
在 FastAPI 中接收请求体
现在,在你的 FastAPI 应用中,创建一个处理 POST 或 PUT 请求的路由,并在函数参数中使用你定义的 Pydantic 模型作为类型提示。FastAPI 会自动:
- 读取请求体的内容 (通常是 JSON)。
- 使用 Pydantic 模型对这些数据进行解析和验证。
- 如果数据有效,将验证后的数据作为一个 Pydantic 模型的实例传递给你的函数参数。
- 如果数据无效(例如,缺少必填字段,或者类型不匹配),FastAPI 会自动返回一个 422 Unprocessable Entity 错误响应,其中包含详细的错误信息。
修改 main.py
:
“`python
main.py (在之前的代码基础上添加或修改)
from fastapi import FastAPI
导入你刚刚定义的 Pydantic 模型
from models import Item, User # 假设你在 models.py 文件中定义了 Item 和 User
app = FastAPI()
… (前面的 GET 路由代码不变)
定义一个处理 POST 请求的路由,路径是 “/items/”
@app.post(“/items/”)
函数参数 item: Item 表示期望请求体是一个符合 Item 模型的数据结构
FastAPI 会自动读取请求体,用 Item 模型验证,并创建一个 Item 实例赋给 item 变量
async def create_item(item: Item):
# 你可以直接访问 item 实例的属性
item_dict = item.dict() # 将 Pydantic 模型转换为 Python 字典
# 可以在这里添加一些处理逻辑,比如计算税费
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({“price_with_tax”: price_with_tax})
# 返回处理后的数据
return item_dict
结合路径参数、查询参数和请求体
@app.put(“/items/{item_id}”)
item_id: int 是路径参数
q: Optional[str] = None 是查询参数
item: Item 是请求体
async def update_item(item_id: int, item: Item, q: Optional[str] = None):
results = {“item_id”: item_id, **item.dict()} # 将 item 模型转为字典并合并
if q:
results.update({“q”: q})
return results
一个简单的创建用户的 POST 请求例子
@app.post(“/users/”)
async def create_user(user: User):
return user
“`
保存文件。Uvicorn 重新加载。访问 http://127.0.0.1:8000/docs
。你会看到 /items/
的 POST 接口和 /items/{item_id}
的 PUT 接口,它们都显示了请求体的结构(基于你的 Pydantic 模型),包括字段名、类型、是否必填等信息。
你可以使用 Swagger UI 来测试这些接口。点击 /items/
POST 接口,然后点击 “Try it out”。在 “Request body” 区域,Swagger UI 会显示一个根据你的 Item
模型自动生成的示例 JSON 数据。你可以修改这些数据并发送请求:
示例请求体 (JSON):
json
{
"name": "Laptop",
"description": "A cool laptop",
"price": 1200.50,
"tax": 120.05
}
发送请求,你应该会收到一个 200 OK 的响应,响应体是你函数返回的 JSON 数据,可能包含了计算后的 price_with_tax
。
尝试发送一个无效的请求体:
json
{
"name": "Tablet",
"description": "Missing price field"
// price 字段是必填的,这里没有提供
}
发送这个请求,你应该会收到一个 422 Unprocessable Entity 错误响应,详细说明了为什么请求体无效(例如,”field required: price”)。这就是 Pydantic 自动验证的作用!
对于 /items/{item_id}
的 PUT 接口,你可以同时在路径中指定 item_id
,在查询参数中提供 q
(如果需要),并在请求体中发送更新后的物品数据。
总结参数处理
现在你已经学会了如何在 FastAPI 中处理不同类型的请求数据:
- 路径参数: URL 路径的一部分,用于标识资源。在函数参数中定义,没有默认值。FastAPI 根据函数参数名匹配路径中的
{}
,并利用类型提示进行转换和验证。 - 查询参数: URL 中
?
后面的键值对,用于过滤、排序等。在函数参数中定义,有默认值 (包括None
)。FastAPI 根据函数参数名匹配查询参数,并利用类型提示进行转换和验证。 - 请求体: 随 POST, PUT, PATCH 等请求发送的数据,通常是 JSON。使用 Pydantic
BaseModel
定义数据结构,并在函数参数中使用该模型作为类型提示。FastAPI 利用 Pydantic 进行验证和解析。
FastAPI 通过简单直观的方式(主要是利用 Python 的类型提示)让你清晰地定义 API 接口的数据契约,并自动完成大量繁琐的数据处理和验证工作,同时生成漂亮的文档。
FastAPI 的其他亮点 (简要提及)
这篇新手教程只涵盖了 FastAPI 的基础功能。FastAPI 还有很多强大的特性值得学习:
- 依赖注入系统 (Dependencies):FastAPI 的依赖注入系统非常强大,可以轻松地实现代码复用、权限验证、数据库会话管理等。
- 安全性与认证:内置了对 OAuth2、JWT、API Key 等多种认证方式的支持,并与依赖注入系统完美结合。
- 数据库集成:虽然 FastAPI 不强制使用特定的数据库或 ORM,但与 SQLAlchemy, SQLModel (FastAPI 作者开发的基于 Pydantic 和 SQLAlchemy 的库) 等集成非常顺畅。
- 测试:FastAPI 提供了 TestClient,可以方便地编写自动化测试代码。
- 错误处理:可以自定义异常处理器来返回特定的错误响应。
- 表单数据、文件上传:支持处理 HTML 表单数据和文件上传。
- CORS, Middleware:可以轻松配置 CORS 策略和使用中间件。
学习完本教程的基础知识后,你可以根据自己的项目需求,逐步深入了解这些高级特性。
下一步:实践与深入
最好的学习方法是实践。现在你已经掌握了 FastAPI 的基础,尝试构建一个简单的 Web 应用吧!你可以尝试构建一个简单的待办事项 (Todo List) API:
- 创建一个 Pydantic 模型
Todo
,包含title
(str),description
(Optional[str]),completed
(bool, default=False) 等字段。 - 使用一个 Python 列表或字典来模拟数据库,存储 Todo 项目。
- 创建以下 API 接口:
POST /todos/
: 创建新的 Todo (接收请求体)。GET /todos/
: 获取所有 Todo (支持查询参数进行过滤或分页)。GET /todos/{todo_id}
: 获取特定 ID 的 Todo (使用路径参数)。PUT /todos/{todo_id}
: 更新特定 ID 的 Todo (使用路径参数和请求体)。DELETE /todos/{todo_id}
: 删除特定 ID 的 Todo (使用路径参数)。
在实践过程中,你会遇到更多问题,解决它们是提高技能的关键。遇到困难时,查阅 FastAPI 官方文档是最好的选择。FastAPI 的官方文档写得非常详细、清晰且友好,是学习 FastAPI 的宝贵资源。
总结
在这篇教程中,我们学习了 FastAPI 的基础知识,包括:
- 为什么选择 FastAPI (高性能、开发效率、自动文档等)。
- 如何安装 FastAPI 和 Uvicorn。
- 如何编写并运行你的第一个 “Hello World” FastAPI 应用。
- 如何访问和利用自动生成的 API 文档 (Swagger UI 和 ReDoc)。
- 如何定义不同 HTTP 方法的路由 (@app.get, @app.post 等)。
- 如何使用路径参数和类型提示来获取 URL 中的动态数据,并实现自动验证。
- 如何使用查询参数和默认值来获取 URL 中的附加数据,并实现自动验证。
- 如何使用 Pydantic
BaseModel
定义请求体的数据结构,并在函数参数中接收请求体,利用 FastAPI 和 Pydantic 进行自动验证和解析。
FastAPI 是一个现代且强大的 Web 框架,它通过充分利用 Python 的特性,让构建高性能 API 变得更加简单愉快。希望这篇新手友好的教程能够帮助你迈出 FastAPI 学习的第一步。
祝你在 FastAPI 的探索之旅中一切顺利!不断实践,不断学习,你将能够构建出令人惊叹的 Web API。