FastAPI教程:Python Web开发新选择 – wiki基地


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的设计哲学凝聚了众多优秀思想:

  1. 快速高效:基于Starlette(用于高性能异步)、Pydantic(用于数据验证和序列化)构建,其性能与NodeJS和GoLang的框架不相上下。
  2. 快速编码:得益于类型提示和自动代码生成,开发速度提升约200%至300%。
  3. 更少错误:约40%的人为(开发者)错误由类型提示在编码阶段捕获。
  4. 智能提示:强大的编辑器支持(如VS Code、PyCharm),自动补全无处不在。
  5. 易学易用:API设计简洁直观,文档清晰详尽。
  6. 代码简洁:最小化代码重复,每个参数声明具有多种功能。
  7. 生产就绪:自动生成交互式API文档(基于OpenAPI和JSON Schema)。
  8. 标准兼容:完全兼容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必须是浮点数,而descriptiontax`是可选的。当请求的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是必需的。
*
limitskip有默认值。
*
tags是可选的列表,并且在URL中使用了别名item-tagsQuery(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()}
``
当你声明一个Pydantic模型作为路径操作函数的参数时,FastAPI会自动:
1. 从请求中读取JSON数据。
2. 根据
Item模型验证数据。如果无效,返回清晰的错误。
3. 将有效数据转换为
Item`类的实例。
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-multipartpip 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
可以使用FileUploadFile

“`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_codedetail和可选的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无疑是当前最耀眼的选择之一。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部