FastAPI教程:Python Web开发新选择
在Python Web开发的星空中,Django和Flask曾是两颗最耀眼的星。Django以其“大而全”的特性,提供了从ORM到后台管理的全套解决方案,适合快速构建复杂的Web应用;Flask则以其“小而美”的微框架特性,给予开发者极高的灵活性和自由度。然而,随着技术的发展,尤其是异步编程和类型提示在Python社区的兴起,开发者们开始寻求更高性能、更现代化、开发体验更佳的Web框架。FastAPI,正是顺应这一趋势而诞生的新星,并迅速获得了广泛的关注和应用。
本文将详细介绍FastAPI,从其核心特性、安装入门,到关键概念和高级用法,旨在为Python Web开发者提供一个全面的FastAPI学习指南,展现其作为Python Web开发新选择的魅力。
什么是FastAPI?
FastAPI是一个现代、快速(高性能)的Web框架,用于基于标准的Python类型提示构建API。它由Sebastián Ramírez (tiangolo) 开发,并于2018年首次发布。FastAPI的设计哲学凝聚了众多优秀思想:
- 快速高效:基于Starlette(用于高性能异步)、Pydantic(用于数据验证和序列化)构建,其性能与NodeJS和GoLang的框架不相上下。
- 快速编码:得益于类型提示和自动代码生成,开发速度提升约200%至300%。
- 更少错误:约40%的人为(开发者)错误由类型提示在编码阶段捕获。
- 智能提示:强大的编辑器支持(如VS Code、PyCharm),自动补全无处不在。
- 易学易用:API设计简洁直观,文档清晰详尽。
- 代码简洁:最小化代码重复,每个参数声明具有多种功能。
- 生产就绪:自动生成交互式API文档(基于OpenAPI和JSON Schema)。
- 标准兼容:完全兼容OpenAPI(以前称为Swagger)和JSON Schema。
FastAPI的核心优势
1. 基于类型提示 (Type Hints)
Python 3.6+ 引入的类型提示是FastAPI的基石。通过为函数参数和返回值添加类型注解,FastAPI能够:
* 数据验证:自动验证请求数据的类型和结构是否符合预期。
* 数据序列化/反序列化:自动将请求数据转换为Python对象,并将Python对象转换为JSON响应。
* 自动生成API文档:根据类型提示生成符合OpenAPI规范的文档。
* 编辑器支持:IDE可以根据类型提示提供更精准的自动补全和错误检查。
例如:
“`python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None # Python 3.10+ 新语法,等同于 Optional[str]
price: float
tax: float | None = None
@app.post(“/items/”)
async def create_item(item: Item):
return item
``
Item
在这个例子中,模型使用了Pydantic,
name必须是字符串,
price必须是浮点数,而
description和
tax`是可选的。当请求的JSON数据不符合这些规则时,FastAPI会自动返回一个包含详细错误信息的422 Unprocessable Entity响应。
2. 异步支持 (Async/Await)
FastAPI从底层就支持异步编程。你可以使用async def
来定义异步路径操作函数,这使得处理I/O密集型任务(如数据库查询、外部API调用)时,应用不会被阻塞,从而获得极高的并发性能。FastAPI构建在ASGI(Asynchronous Server Gateway Interface)规范之上,推荐使用Uvicorn作为ASGI服务器运行。
“`python
import asyncio
@app.get(“/slow-data/”)
async def get_slow_data():
await asyncio.sleep(5) # 模拟I/O密集型操作
return {“message”: “Data loaded after 5 seconds”}
``
asyncio.sleep(5)`,服务器仍然可以处理其他并发请求。
即使某个请求在等待
3. 依赖注入 (Dependency Injection)
FastAPI拥有一个非常强大且易于使用的依赖注入系统。这使得代码组织更清晰、可重用性更高、测试也更容易。依赖项可以是普通的函数,也可以是async def
函数,它们可以返回值,也可以不返回值(例如,仅执行某些操作)。
常见的依赖注入用例包括:
* 共享数据库连接。
* 用户认证和授权。
* 获取通用参数。
“`python
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
async def get_current_user(x_token: str = Header(…)):
if x_token != “fake-super-secret-token”:
raise HTTPException(status_code=400, detail=”X-Token header invalid”)
return {“username”: “fakeuser”}
@app.get(“/users/me”)
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
``
get_current_user是一个依赖项,它从请求头中获取
X-Token并进行验证。
read_users_me路径操作函数通过
Depends(get_current_user)声明了对这个依赖项的依赖。FastAPI会在调用
read_users_me之前先执行
get_current_user`。
4. 自动交互式API文档
这是FastAPI最受欢迎的特性之一。只需运行FastAPI应用,它就会自动为你生成两种交互式的API文档界面:
* Swagger UI (访问 /docs
路径)
* ReDoc (访问 /redoc
路径)
这些文档是根据你的代码(路径、参数、请求体、响应模型、类型提示)自动生成的,并且完全符合OpenAPI规范。你可以在这些界面上直接测试API端点,查看请求和响应的结构。这极大地简化了API的调试、协作和分享过程。
安装与快速入门
1. 安装
首先,你需要安装FastAPI本身以及一个ASGI服务器,如Uvicorn:
bash
pip install fastapi uvicorn[standard]
uvicorn[standard]
会安装Uvicorn并附带一些推荐的依赖,如httptools
(用于更快的HTTP解析)和websockets
(如果需要WebSocket支持)。
2. “Hello World”
创建一个名为main.py
的文件:
“`python
from fastapi import FastAPI
1. 创建 FastAPI 实例
app = FastAPI()
2. 定义路径操作 (Path Operation)
@app.get(“/”) # @app 是FastAPI实例,.get表示处理HTTP GET请求,”/”是路径
async def read_root(): # async def 定义异步函数,也可以用普通 def
return {“Hello”: “World”}
@app.get(“/items/{item_id}”)
async def read_item(item_id: int, q: str | None = None):
# item_id 是路径参数,会自动进行类型转换和验证
# q 是查询参数,可选,默认为 None
item_data = {“item_id”: item_id}
if q:
item_data.update({“q”: q})
return item_data
“`
3. 运行服务
在终端中,进入main.py
所在的目录,然后运行:
bash
uvicorn main:app --reload
* main
: 指的是main.py
文件(Python模块)。
* app
: 指的是在main.py
中创建的FastAPI实例 app = FastAPI()
。
* --reload
: 使服务器在代码更改后自动重启,非常适合开发阶段。
现在,打开浏览器访问:
* http://127.0.0.1:8000/
: 你会看到 {"Hello": "World"}
。
* http://127.0.0.1:8000/items/5?q=somequery
: 你会看到 {"item_id": 5, "q": "somequery"}
。
* http://127.0.0.1:8000/docs
: 你会看到Swagger UI自动生成的交互式API文档。
* http://127.0.0.1:8000/redoc
: 你会看到ReDoc自动生成的API文档。
FastAPI核心概念详解
1. 路径操作装饰器
FastAPI使用装饰器来定义API端点。常用的HTTP方法都有对应的装饰器:
* @app.get()
* @app.post()
* @app.put()
* @app.delete()
* @app.options()
* @app.head()
* @app.patch()
* @app.trace()
2. 路径参数 (Path Parameters)
路径参数是URL路径的一部分,用花括号{}
表示。
python
@app.get("/users/{user_id}")
async def get_user(user_id: int): # user_id 会被自动转换为 int
return {"user_id": user_id}
如果访问/users/abc
,由于abc
无法转换为int
,FastAPI会自动返回一个422错误。
3. 查询参数 (Query Parameters)
查询参数是URL中?
之后的部分,以key=value
形式出现,用&
分隔。在FastAPI中,函数参数如果不是路径参数,并且具有默认值(或类型提示为Optional
/None
),则被视为查询参数。
“`python
from typing import List, Optional # Python 3.8及以下需要
from fastapi import FastAPI, Query
app = FastAPI()
@app.get(“/search/”)
async def search_items(
keyword: str, # 必需的查询参数
limit: int = 10, # 带默认值的可选查询参数
skip: int = 0,
tags: Optional[List[str]] = Query(None, alias=”item-tags”) # 高级查询参数,带别名
):
results = {“keyword”: keyword, “limit”: limit, “skip”: skip}
if tags:
results.update({“tags”: tags})
return results
``
http://127.0.0.1:8000/search/?keyword=python&item-tags=web&item-tags=api
访问示例:*
keyword是必需的。
limit
*和
skip有默认值。
tags
*是可选的列表,并且在URL中使用了别名
item-tags。
Query(None, …)表示默认值为
None`。
Query
还允许进行更复杂的验证,如最小/最大长度、正则表达式等:
Query(None, min_length=3, max_length=50, regex="^fixedquery$")
4. 请求体 (Request Body)
当需要客户端向API发送数据时(通常用于POST、PUT、PATCH请求),这些数据会包含在请求体中。FastAPI使用Pydantic模型来定义和验证请求体。
“`python
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Optional
class Item(BaseModel):
name: str
description: Optional[str] = Field(None, title=”The description of the item”, max_length=300)
price: float = Field(…, gt=0, description=”The price must be greater than zero”) # … 表示必需
tax: Optional[float] = None
app = FastAPI()
@app.post(“/items/”)
async def create_item(item: Item): # item 参数的类型是 Item 模型
item_dict = item.dict()
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}”)
async def update_item(item_id: int, item: Item):
return {“item_id”: item_id, **item.dict()}
``
Item
当你声明一个Pydantic模型作为路径操作函数的参数时,FastAPI会自动:
1. 从请求中读取JSON数据。
2. 根据模型验证数据。如果无效,返回清晰的错误。
Item`类的实例。
3. 将有效数据转换为
4. 将该实例传递给你的函数。
5. 在文档中自动生成请求体的JSON Schema。
Field
可以用来为Pydantic模型的字段添加额外的元数据和验证,如title
, description
, gt
(greater than), lt
(less than) 等。
5. 响应模型 (Response Model)
你可以指定API端点返回的数据结构,这有助于:
* 数据过滤:只返回模型中定义的字段,即使函数内部返回了更多数据。
* 数据验证:确保响应数据符合定义的结构(尽管主要用于输出,验证不如输入严格)。
* 文档化:在API文档中清晰地指明响应的Schema。
* 数据转换:Pydantic模型会自动处理数据转换(例如,将Python datetime
对象转换为ISO 8601字符串)。
“`python
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
class UserIn(BaseModel): # 输入模型
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel): # 输出模型
username: str
email: EmailStr
full_name: str | None = None
app = FastAPI()
模拟数据库
fake_users_db = {}
@app.post(“/users/”, response_model=UserOut) # 使用 response_model
async def create_user(user: UserIn):
# 实际应用中,这里会保存到数据库
# user.dict() 包含了 UserIn 的所有字段,包括 password
fake_users_db[user.username] = user.dict()
return user # FastAPI 会根据 UserOut 过滤数据,password 不会返回
``
create_user
在这个例子中,即使函数返回了包含
password的完整
user对象(Pydantic模型实例),由于
response_model=UserOut的指定,FastAPI会自动过滤掉
password字段,只返回
UserOut`模型中定义的字段。
6. 表单数据 (Form Data)
当需要处理HTML表单提交的数据 ( application/x-www-form-urlencoded
) 时,可以使用Form
。
首先需要安装 python-multipart
:pip install python-multipart
“`python
from fastapi import FastAPI, Form
app = FastAPI()
@app.post(“/login/”)
async def login(username: str = Form(…), password: str = Form(…)):
return {“username”: username}
``
Form(…)的用法与
Path(…)和
Query(…)类似,
…` 表示该字段是必需的。
7. 文件上传 (File Uploads)
FastAPI支持文件上传,同样需要python-multipart
。
可以使用File
和UploadFile
。
“`python
from fastapi import FastAPI, File, UploadFile
from typing import List
app = FastAPI()
@app.post(“/files/”)
async def create_file(file: bytes = File(…)): # 较小的文件,直接作为 bytes
return {“file_size”: len(file)}
@app.post(“/uploadfile/”)
async def create_upload_file(file: UploadFile = File(…)): # 较大的文件,UploadFile提供更多功能
# content = await file.read() # 读取文件内容
# file.filename, file.content_type
return {“filename”: file.filename, “content_type”: file.content_type}
@app.post(“/uploadfiles/”)
async def create_upload_files(files: List[UploadFile] = File(…)):
return [{“filename”: file.filename} for file in files]
``
UploadFile相比
bytes`更适合处理大文件,因为它会将文件内容存到内存或磁盘(取决于文件大小),并提供异步方法。
8. 错误处理 (Error Handling)
FastAPI会自动处理Pydantic验证错误,并返回JSON格式的错误响应。对于自定义的HTTP错误,可以使用HTTPException
。
“`python
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {“foo”: “The Foo Wrestlers”}
@app.get(“/items/{item_id}”)
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail=”Item not found”)
return {“item”: items[item_id]}
``
HTTPException可以接受
status_code、
detail和可选的
headers`参数。
9. 中间件 (Middleware)
中间件是一个函数,它处理每个进入应用的请求,并在生成响应之前或之后对其进行操作。
FastAPI使用Starlette的中间件。
“`python
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware(“http”)
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request) # 处理请求
process_time = time.time() – start_time
response.headers[“X-Process-Time”] = str(process_time)
return response
@app.get(“/”)
async def main():
return {“message”: “Hello World”}
``
X-Process-Time`响应头返回。
这个中间件会计算每个请求的处理时间,并将其作为
10. CORS (Cross-Origin Resource Sharing)
如果你的前端应用和后端API部署在不同的域名下,就需要配置CORS。FastAPI提供了CORSMiddleware
。
“`python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
“http://localhost”,
“http://localhost:8080”, # 假设你的前端运行在这个地址
“https://your-frontend-domain.com”,
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 允许的源列表,”” 表示所有
allow_credentials=True, # 是否支持 cookie 跨域
allow_methods=[““], # 允许的 HTTP 方法
allow_headers=[“*”], # 允许的 HTTP 请求头
)
@app.get(“/”)
async def main():
return {“message”: “Hello World with CORS”}
“`
高级特性与最佳实践
1. 依赖注入的进阶
- 类作为依赖项:你可以定义一个类,FastAPI会像处理函数依赖一样处理它(调用类的实例)。
- 子依赖项:依赖项可以依赖于其他依赖项。
- 全局依赖项:可以在整个
FastAPI
应用或APIRouter
上声明依赖项,它们将应用于所有路径操作。
“`python
from fastapi import FastAPI, Depends, APIRouter
app = FastAPI()
router = APIRouter()
async def verify_token(token: str = Depends(oauth2_scheme)): # 假设 oauth2_scheme 已定义
# … token 验证逻辑 …
pass
应用于整个路由器的依赖
app_router = APIRouter(dependencies=[Depends(verify_token)])
@app_router.get(“/users/me”)
async def get_current_user_data():
return {“user_data”: “secret”}
app.include_router(app_router)
“`
2. APIRouter 组织代码
当应用变得庞大时,可以将路径操作分散到多个模块中,使用APIRouter
。
routers/items.py
:
“`python
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter(
prefix=”/items”, # 所有此路由器的路径都会带上/items前缀
tags=[“items”], # 在API文档中分组
responses={404: {“description”: “Not found”}}, # 默认响应
)
class Item(BaseModel):
name: str
price: float
@router.get(“/{item_id}”)
async def read_item(item_id: str):
return {“name”: “Fake Item”, “item_id”: item_id}
`main.py`:
python
from fastapi import FastAPI
from routers import items # 假设 items.py 在 routers 目录下
app = FastAPI()
app.include_router(items.router)
“`
3. 后台任务 (Background Tasks)
对于不需要立即完成并且不应阻塞响应的操作(如发送邮件通知),可以使用后台任务。
“`python
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def write_notification(email: str, message=””):
with open(“log.txt”, mode=”a”) as email_file:
content = f”notification for {email}: {message}\n”
email_file.write(content)
@app.post(“/send-notification/{email}”)
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message=”some notification”)
return {“message”: “Notification sent in the background”}
``
background_tasks.add_task()`会将任务添加到后台执行,而API会立即返回响应。
4. 数据库集成
FastAPI本身不绑定任何特定的数据库或ORM。你可以自由选择:
* SQLAlchemy (最流行的Python ORM)
* Tortoise ORM (类似Django ORM的异步ORM)
* Databases (异步SQL查询构建器)
* MongoDB驱动 (如Motor,用于异步MongoDB操作)
通常,你会创建一个依赖项来管理数据库会话/连接。
5. 测试
FastAPI应用非常容易测试,因为它基于Starlette,而Starlette提供了TestClient
。
“`python
from fastapi.testclient import TestClient
from .main import app # 假设你的 FastAPI app 实例在 main.py
client = TestClient(app)
def test_read_main():
response = client.get(“/”)
assert response.status_code == 200
assert response.json() == {“Hello”: “World”}
def test_read_item():
response = client.get(“/items/foo”, headers={“X-Token”: “coneofsilence”})
assert response.status_code == 200
assert response.json() == {
“id”: “foo”,
“title”: “Foo”,
“description”: “There goes my hero”,
}
``
TestClient`可以直接在测试代码中调用你的API端点,就像通过HTTP请求一样,但速度更快,因为它在内部直接调用应用。
FastAPI 与 Flask/Django 的简要比较
- Flask:
- 相似点:都是微框架,灵活,不强制特定项目结构。
- FastAPI优势:内置异步支持、数据验证(Pydantic)、自动API文档、依赖注入系统。Flask需要通过扩展来实现这些功能,集成度不如FastAPI。性能上,FastAPI通常由于其异步特性和Starlette/Pydantic的优化而表现更好。
- Django:
- 相似点:Django REST framework (DRF) 提供了类似FastAPI的API构建能力。
- FastAPI优势:更现代的Python特性(类型提示、async/await)、性能更高、自动文档生成更出色、学习曲线相对平缓(对于仅构建API而言)。Django更像一个全家桶,包含了ORM、管理后台等,对于需要这些完整功能的项目,Django依然强大。DRF的序列化和验证系统虽然强大,但Pydantic在简洁性和与类型系统的集成方面更胜一筹。
总结:为何选择FastAPI?
FastAPI凭借其对现代Python特性的出色运用(类型提示、异步编程),结合Pydantic的数据验证能力和Starlette的高性能ASGI基础,为Python Web开发带来了全新的体验。
选择FastAPI的理由:
1. 卓越的性能:接近Go和Node.js的性能水平。
2. 极高的开发效率:类型提示带来的智能补全和错误检查,Pydantic减少了大量样板代码。
3. 自动化的API文档:Swagger UI 和 ReDoc 开箱即用,极大方便了API的调试和协作。
4. 现代化的开发体验:充分利用Python新特性,代码更健壮、可读性更高。
5. 强大的依赖注入系统:代码解耦,易于测试和维护。
6. 活跃的社区和优秀的文档:学习资源丰富,遇到问题容易找到解决方案。
对于需要构建高性能、高可靠性、易于维护的API服务的项目,无论是初创公司追求快速迭代,还是大型企业寻求现代化技术栈,FastAPI都是一个值得认真考虑的Python Web开发新选择。它不仅让开发过程更愉悦,也让最终的产品更出色。如果你正在寻找一个能够充分发挥Python 3.6+潜力的Web框架,那么FastAPI无疑是当前最耀眼的选择之一。