FastAPI 教程:从零开始构建 RESTful API
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,使用标准 Python 类型提示。 它具有诸多优点,例如:
- 速度快: 性能媲美 Node.js 和 Go (基于 Starlette 和 Pydantic)。
- 易于编码: 开发速度提高约 200% 至 300%。
- 更少的 Bug: 减少约 40% 的人为错误。
- 直观: 强大的编辑器支持,可以自动完成代码。
- 简单: 设计得易于学习和使用。
- 健壮: 用于生产环境的代码。
- 基于标准: 基于并完全兼容 API 的开放标准:OpenAPI 和 JSON Schema。
本教程将带你从零开始构建一个简单的 RESTful API,让你了解 FastAPI 的核心概念和用法。我们将创建一个简单的博客文章 API,它具有创建、读取、更新和删除文章的功能。
环境准备
在开始之前,你需要确保已经安装了 Python 3.7+。然后,你需要安装 FastAPI 和 Uvicorn(一个 ASGI 服务器,用于运行 FastAPI 应用)。
bash
pip install fastapi uvicorn
项目结构
首先,创建一个项目文件夹,并在其中创建一个名为 main.py
的文件,用于存放你的 FastAPI 应用代码。
my_blog_api/
├── main.py
└── ...
创建 FastAPI 应用实例
在 main.py
文件中,导入 FastAPI
类并创建一个应用实例。
“`python
from fastapi import FastAPI
app = FastAPI()
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
“`
这几行代码做了以下几件事:
from fastapi import FastAPI
: 从 FastAPI 库导入FastAPI
类。app = FastAPI()
: 创建一个FastAPI
类的实例,并将其赋值给变量app
。这个app
对象将是你的 API 的核心。@app.get("/")
: 这是一个装饰器,它将下面的函数与 HTTP GET 方法和根路径/
关联起来。这表示当客户端向/
发送 GET 请求时,该函数将被执行。async def read_root():
: 定义一个异步函数read_root
。异步函数是 FastAPI 的推荐用法,可以提高并发性能。return {"message": "Welcome to my blog API!"}
: 函数返回一个 Python 字典。FastAPI 会自动将这个字典转换为 JSON 格式并作为 HTTP 响应返回给客户端。
运行 FastAPI 应用
在终端中,使用 Uvicorn 运行你的 FastAPI 应用。
bash
uvicorn main:app --reload
uvicorn main:app
: 告诉 Uvicorn 运行main.py
文件中的app
对象。--reload
: 启用自动重载功能。当你的代码发生更改时,Uvicorn 会自动重启服务器,这对于开发非常方便。
现在,在你的浏览器中访问 http://localhost:8000/
,你将会看到 JSON 响应 {"message": "Welcome to my blog API!"}
。
添加 API 端点
接下来,我们将添加更多的 API 端点来处理博客文章。
1. 创建博客文章的数据模型
我们需要定义一个数据模型来表示博客文章。我们可以使用 Pydantic 来完成这个任务。 Pydantic 是一个数据验证和设置管理库,它与 FastAPI 集成得很好。
“`python
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
“`
from pydantic import BaseModel
: 从 Pydantic 库导入BaseModel
类。class Post(BaseModel):
: 定义一个名为Post
的类,它继承自BaseModel
。Post
类将用于定义博客文章的数据结构。id: int
: 定义id
字段,类型为整数。title: str
: 定义title
字段,类型为字符串。content: str
: 定义content
字段,类型为字符串。published: bool = True
: 定义published
字段,类型为布尔值,默认值为True
。rating: Optional[int] = None
: 定义rating
字段,类型为可选的整数,默认值为None
。Optional
来自typing
模块,表示该字段可以为None
。
2. 创建存储博客文章的内存数据库
为了简单起见,我们将使用一个简单的列表来模拟数据库。在实际应用中,你可能会使用真正的数据库,例如 PostgreSQL 或 MySQL。
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
“`
posts = []
: 创建一个名为posts
的空列表。这个列表将用于存储博客文章。
3. 创建 POST 端点来创建博客文章
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
“`
@app.post("/posts")
: 这是一个装饰器,它将下面的函数与 HTTP POST 方法和路径/posts
关联起来。这表示当客户端向/posts
发送 POST 请求时,该函数将被执行。async def create_post(post: Post):
: 定义一个异步函数create_post
。它接受一个Post
类型的参数post
。FastAPI 会自动将客户端发送的 JSON 请求体解析为Post
对象。posts.append(post)
: 将post
对象添加到posts
列表中。return post
: 返回post
对象。FastAPI 会自动将这个对象转换为 JSON 格式并作为 HTTP 响应返回给客户端。
4. 创建 GET 端点来获取所有博客文章
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
@app.get(“/posts”)
async def get_posts():
return posts
“`
@app.get("/posts")
: 这是一个装饰器,它将下面的函数与 HTTP GET 方法和路径/posts
关联起来。这表示当客户端向/posts
发送 GET 请求时,该函数将被执行。async def get_posts():
: 定义一个异步函数get_posts
。return posts
: 返回posts
列表。FastAPI 会自动将这个列表转换为 JSON 格式并作为 HTTP 响应返回给客户端。
5. 创建 GET 端点来获取单个博客文章
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
@app.get(“/posts”)
async def get_posts():
return posts
@app.get(“/posts/{post_id}”)
async def get_post(post_id: int):
for post in posts:
if post.id == post_id:
return post
raise HTTPException(status_code=404, detail=”Post not found”)
“`
@app.get("/posts/{post_id}")
: 这是一个装饰器,它将下面的函数与 HTTP GET 方法和路径/posts/{post_id}
关联起来。{post_id}
是一个路径参数,它允许我们从 URL 中提取post_id
的值。async def get_post(post_id: int):
: 定义一个异步函数get_post
。它接受一个整数类型的参数post_id
。for post in posts:
: 循环遍历posts
列表。if post.id == post_id:
: 检查当前博客文章的id
是否等于请求的post_id
。return post
: 如果找到匹配的博客文章,则返回该文章。raise HTTPException(status_code=404, detail="Post not found")
: 如果没有找到匹配的博客文章,则抛出一个HTTPException
异常。HTTPException
是 FastAPI 提供的异常类,它可以用于返回 HTTP 错误响应。在这里,我们返回一个 404 Not Found 错误,并附带一条消息 “Post not found”。
6. 创建 PUT 端点来更新博客文章
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
@app.get(“/posts”)
async def get_posts():
return posts
@app.get(“/posts/{post_id}”)
async def get_post(post_id: int):
for post in posts:
if post.id == post_id:
return post
raise HTTPException(status_code=404, detail=”Post not found”)
@app.put(“/posts/{post_id}”)
async def update_post(post_id: int, updated_post: Post):
for i, post in enumerate(posts):
if post.id == post_id:
posts[i] = updated_post
return updated_post
raise HTTPException(status_code=404, detail=”Post not found”)
“`
@app.put("/posts/{post_id}")
: 这是一个装饰器,它将下面的函数与 HTTP PUT 方法和路径/posts/{post_id}
关联起来。async def update_post(post_id: int, updated_post: Post):
: 定义一个异步函数update_post
。它接受两个参数:一个整数类型的post_id
和一个Post
类型的updated_post
。for i, post in enumerate(posts):
: 循环遍历posts
列表,并使用enumerate
函数获取索引i
和博客文章post
。if post.id == post_id:
: 检查当前博客文章的id
是否等于请求的post_id
。posts[i] = updated_post
: 如果找到匹配的博客文章,则使用updated_post
替换该文章。return updated_post
: 返回updated_post
对象。raise HTTPException(status_code=404, detail="Post not found")
: 如果没有找到匹配的博客文章,则抛出一个HTTPException
异常。
7. 创建 DELETE 端点来删除博客文章
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
@app.get(“/posts”)
async def get_posts():
return posts
@app.get(“/posts/{post_id}”)
async def get_post(post_id: int):
for post in posts:
if post.id == post_id:
return post
raise HTTPException(status_code=404, detail=”Post not found”)
@app.put(“/posts/{post_id}”)
async def update_post(post_id: int, updated_post: Post):
for i, post in enumerate(posts):
if post.id == post_id:
posts[i] = updated_post
return updated_post
raise HTTPException(status_code=404, detail=”Post not found”)
@app.delete(“/posts/{post_id}”)
async def delete_post(post_id: int):
for i, post in enumerate(posts):
if post.id == post_id:
posts.pop(i)
return {“message”: “Post deleted successfully”}
raise HTTPException(status_code=404, detail=”Post not found”)
“`
@app.delete("/posts/{post_id}")
: 这是一个装饰器,它将下面的函数与 HTTP DELETE 方法和路径/posts/{post_id}
关联起来。async def delete_post(post_id: int):
: 定义一个异步函数delete_post
。它接受一个整数类型的参数post_id
。for i, post in enumerate(posts):
: 循环遍历posts
列表,并使用enumerate
函数获取索引i
和博客文章post
。if post.id == post_id:
: 检查当前博客文章的id
是否等于请求的post_id
。posts.pop(i)
: 如果找到匹配的博客文章,则从posts
列表中删除该文章。return {"message": "Post deleted successfully"}
: 返回一个包含消息 “Post deleted successfully” 的字典。raise HTTPException(status_code=404, detail="Post not found")
: 如果没有找到匹配的博客文章,则抛出一个HTTPException
异常。
完整的代码
以下是 main.py
文件的完整代码:
“`python
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Post(BaseModel):
id: int
title: str
content: str
published: bool = True
rating: Optional[int] = None
posts = [] # 用于存储博客文章的列表
@app.get(“/”)
async def read_root():
return {“message”: “Welcome to my blog API!”}
@app.post(“/posts”)
async def create_post(post: Post):
posts.append(post)
return post
@app.get(“/posts”)
async def get_posts():
return posts
@app.get(“/posts/{post_id}”)
async def get_post(post_id: int):
for post in posts:
if post.id == post_id:
return post
raise HTTPException(status_code=404, detail=”Post not found”)
@app.put(“/posts/{post_id}”)
async def update_post(post_id: int, updated_post: Post):
for i, post in enumerate(posts):
if post.id == post_id:
posts[i] = updated_post
return updated_post
raise HTTPException(status_code=404, detail=”Post not found”)
@app.delete(“/posts/{post_id}”)
async def delete_post(post_id: int):
for i, post in enumerate(posts):
if post.id == post_id:
posts.pop(i)
return {“message”: “Post deleted successfully”}
raise HTTPException(status_code=404, detail=”Post not found”)
“`
使用 API 文档
FastAPI 自动生成交互式的 API 文档。 你可以在 http://localhost:8000/docs
访问它。 你可以使用这个文档来测试你的 API 端点。
总结
本教程介绍了使用 FastAPI 从零开始构建 RESTful API 的基本步骤。 你学习了如何:
- 创建 FastAPI 应用实例。
- 定义数据模型。
- 创建 API 端点来处理不同的 HTTP 方法(GET, POST, PUT, DELETE)。
- 使用 Pydantic 进行数据验证。
- 使用 HTTPException 返回错误响应。
- 利用 FastAPI 自动生成的 API 文档。
这只是 FastAPI 的一个入门教程。 FastAPI 还有许多其他功能,例如依赖注入、安全性、中间件等。 建议你阅读 FastAPI 的官方文档来了解更多信息。
希望本教程对你有所帮助!