在前面的章节中,我们已经了解了FastAPI的基本概念,搭建了开发环境,并创建了第一个简单的FastAPI应用。现在,是时候深入探索FastAPI的核心特性了。 这些特性是FastAPI强大功能的基石,理解它们将帮助我们构建更加复杂和强大的API应用。FastAPI的核心特性包括路由系统、请求和响应处理、异步编程支持、自动文档生成、数据验证等。 在这一部分中,我们将系统地学习这些特性,理解它们的工作原理,掌握它们的使用方法。
路由系统是Web框架的核心,它决定了如何将HTTP请求映射到处理函数。FastAPI的路由系统既强大又灵活,它支持多种HTTP方法、路径参数、查询参数、请求体等。理解FastAPI的路由系统是掌握FastAPI的第一步,也是最重要的一步。

FastAPI支持所有标准的HTTP方法,包括GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS等。我们使用装饰器来定义路由,每个装饰器对应一个HTTP方法。例如,@app.get()用于处理GET请求,@app.post()用于处理POST请求。这种设计直观明了,使得代码易于阅读和理解。
当我们定义一个路由时,我们需要指定路径和处理函数。路径可以是静态的,比如/users,也可以是动态的,比如/users/{user_id}。FastAPI会自动解析路径参数,并将它们传递给处理函数。这种自动解析是FastAPI的便利特性之一,我们不需要手动解析URL。
处理函数可以是同步的,也可以是异步的。对于I/O密集型操作,我们应该使用异步函数,这样可以提高应用的并发处理能力。对于CPU密集型操作,同步函数可能更合适,因为Python的全局解释器锁(GIL)会限制多线程的性能。FastAPI会自动识别函数是同步还是异步,并采用相应的处理方式。
让我们通过一个完整的例子来理解不同HTTP方法的使用。假设我们正在构建一个用户管理API,我们需要实现用户的创建、读取、更新和删除操作。我们可以这样定义路由:
|from fastapi import FastAPI from pydantic import BaseModel from typing import Optional app = FastAPI() class User(BaseModel): id: Optional[int] = None name: str email: str age: int # 存储用户的简单字典(实际应用中应该使用数据库) users_db = {} @app.get("/users/{user_id}") def get_user(user_id: int): """获取指定ID的用户信息""" if user_id not in users_db: from fastapi import HTTPException raise HTTPException(status_code=404, detail="User not found") return users_db[user_id] @app.post("/users/") def create_user(user: User): """创建新用户""" user_id = len(users_db) + 1 user.id = user_id users_db[user_id] = user return {"message": "User created", "user": user} @app.put("/users/{user_id}") def update_user(user_id: int, user: User): """更新指定ID的用户信息""" if user_id not in users_db: from fastapi import HTTPException raise HTTPException(status_code=404, detail="User not found") user.id = user_id users_db[user_id] = user return {"message": "User updated", "user": user} @app.delete("/users/{user_id}") def delete_user(user_id: int): """删除指定ID的用户""" if user_id not in users_db: from fastapi import HTTPException raise HTTPException(status_code=404, detail="User not found") del users_db[user_id] return {"message": "User deleted"} @app.patch("/users/{user_id}") def partial_update_user(user_id: int, user: dict): """部分更新用户信息(只更新提供的字段)""" if user_id not in users_db: from fastapi import HTTPException raise HTTPException(status_code=404, detail="User not found") existing_user = users_db[user_id].dict() existing_user.update(user) users_db[user_id] = User(**existing_user) return {"message": "User partially updated", "user": users_db[user_id]}
在这个例子中,我们定义了五个不同的端点,分别对应GET、POST、PUT、DELETE和PATCH方法。每个方法都有其特定的用途:GET用于检索数据,POST用于创建新资源,PUT用于完整更新资源,DELETE用于删除资源,PATCH用于部分更新资源。这种设计符合RESTful API的最佳实践。
让我们通过实际的代码示例来理解不同HTTP方法的使用。下面是一个完整的示例,展示了如何定义各种HTTP方法的端点:
|from fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional app = FastAPI() # 数据模型 class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None class ItemUpdate
这个示例展示了如何为同一个资源定义不同的HTTP方法。GET方法用于获取数据,POST方法用于创建新资源,PUT方法用于完整更新,PATCH方法用于部分更新,DELETE方法用于删除资源。每个方法都有其特定的语义和用途,这种设计符合RESTful API的最佳实践。
路径参数是URL路径中的动态部分,它们允许我们创建动态路由。在FastAPI中,我们使用花括号来定义路径参数,比如/users/{user_id}。FastAPI会自动提取路径参数的值,并根据类型提示进行类型转换和验证。
路径参数的类型转换是自动的。如果我们在函数参数中指定了类型,比如user_id: int,FastAPI会尝试将URL中的字符串转换为整数。如果转换失败,FastAPI会返回422错误,并提供详细的错误信息。这种自动类型转换和验证大大简化了我们的代码,我们不需要手动处理类型转换和验证。
路径参数还可以有默认值,虽然这在实践中不太常见。如果路径参数是可选的,我们通常使用查询参数而不是路径参数。但是,在某些情况下,可选路径参数可能是有用的,比如在RESTful API中,某些资源可能有可选的标识符。
路径参数的类型可以是任何Python基本类型,包括int、float、str、bool等。FastAPI还支持更复杂的类型,比如UUID、日期时间等。对于这些类型,FastAPI会自动进行格式验证,确保参数值符合预期的格式。
让我们通过几个例子来理解路径参数的各种用法。首先是一个简单的整数路径参数:
|from fastapi import FastAPI from datetime import datetime from uuid import UUID app = FastAPI() @app.get("/items/{item_id}") def get_item(item_id: int): """获取指定ID的物品,item_id会自动转换为整数""" return {"item_id": item_id, "message": f"Item {item_id} retrieved"
对于更复杂的类型,比如UUID和日期时间,FastAPI也能自动处理:
|from uuid import UUID from datetime import date @app.get("/orders/{order_id}") def get_order(order_id: UUID): """使用UUID作为路径参数,FastAPI会自动验证UUID格式""" return {"order_id": str(order_id), "status": "processing"} @app.get("/events/{event_date}") def get_event(event_date: date):
如果路径参数的类型转换失败,FastAPI会自动返回422错误。例如,如果我们访问/items/abc,而item_id被定义为int类型,FastAPI会返回详细的错误信息,说明无法将"abc"转换为整数。这种自动验证和错误处理大大简化了我们的开发工作。
让我们通过代码示例来理解路径参数的各种用法:
|from fastapi import FastAPI from datetime import datetime from uuid import UUID from typing import Optional app = FastAPI() # 基本类型路径参数 @app.get("/users/{user_id}") def get_user(user_id: int): """整数类型的路径参数""" return {"user_id": user_id, "type":
这些示例展示了路径参数的各种用法。我们可以看到,FastAPI会自动处理类型转换,如果URL中的值无法转换为指定的类型,FastAPI会返回422错误。对于UUID这样的复杂类型,FastAPI还会验证格式是否正确。这种自动验证大大简化了我们的代码,提高了API的健壮性。
查询参数是URL中问号后面的键值对,它们用于传递可选的数据。在FastAPI中,我们只需要在函数参数中定义查询参数,FastAPI会自动从URL中提取它们。如果查询参数有默认值,它就是可选的;如果没有默认值,它就是必需的。
查询参数的类型转换和验证与路径参数类似。FastAPI会根据类型提示自动进行类型转换和验证。如果类型转换失败或验证失败,FastAPI会返回详细的错误信息。这种自动处理使得我们不需要编写大量的样板代码。
查询参数还可以有多个值,这在某些场景下很有用。例如,我们可以使用?tags=python&tags=fastapi来传递多个标签。在FastAPI中,我们可以使用List[str]类型来接收多个值。FastAPI会自动将多个值组合成列表,使得处理变得简单。
查询参数的验证可以更加复杂。我们可以使用Pydantic模型来定义查询参数的结构,这样可以进行更复杂的验证。例如,我们可以验证参数的范围、格式、长度等。这种验证能力使得我们可以构建更加健壮的API。
让我们通过实际例子来理解查询参数的各种用法。首先是一个简单的查询参数示例:
|from fastapi import FastAPI, Query from typing import Optional, List from pydantic import BaseModel app = FastAPI() @app.get("/items/") def get_items(skip: int = 0, limit: int = 10): """基本的查询参数,带有默认值""" return {"skip": skip, "limit": limit, "items": []}
对于需要验证的查询参数,我们可以使用Query函数来添加验证规则:
|@app.get("/products/") def get_products( page: int = Query(1, ge=1, description="页码,必须大于等于1"), page_size: int = Query(10, ge=1, le=100, description="每页数量,1-100之间"), sort_by: str =
对于多个值的查询参数,我们可以使用列表类型:
|@app.get("/articles/") def get_articles( tags: List[str] = Query([], description="文章标签列表"), categories: Optional[List[int]] = None ): """接收多个值的查询参数""" return { "tags": tags, "categories": categories or [], "articles": [] }
我们还可以使用Pydantic模型来定义复杂的查询参数结构:
|from pydantic import BaseModel, Field class ItemQuery(BaseModel): min_price: Optional[float] = Field(None, ge=0, description="最低价格") max_price: Optional[float] = Field(None, ge=0, description="最高价格") category: Optional[str
这些例子展示了查询参数的强大功能。FastAPI会自动处理类型转换、验证、文档生成等所有细节,我们只需要定义参数的类型和验证规则即可。
下面是一些查询参数使用的实际示例:
|from fastapi import FastAPI, Query from typing import List, Optional from pydantic import BaseModel app = FastAPI() # 基本查询参数 @app.get("/items/") def get_items(skip: int = 0, limit: int = 10): """带默认值的查询参数,都是可选的""" return {"skip": skip, "limit": limit, "message"
这些示例展示了查询参数的各种用法。我们可以看到,FastAPI会自动处理类型转换,支持可选参数和必需参数,支持多个值的参数,还支持使用Query函数进行高级验证。这种灵活性使得我们可以构建功能强大且易于使用的API。
随着应用的增长,路由的数量也会增加。为了保持代码的组织性,我们需要将路由组织成模块。FastAPI提供了APIRouter类来实现路由的组织。我们可以创建多个路由器,每个路由器负责一组相关的路由,然后将这些路由器包含到主应用中。
使用APIRouter的好处是多方面的。首先,它使得代码更加模块化,每个模块负责特定的功能领域。其次,它使得代码更加可维护,我们可以独立地开发和测试每个模块。最后,它使得代码更加可重用,我们可以在不同的应用中重用路由器。
APIRouter的使用方式与FastAPI应用类似。我们创建路由器实例,然后使用装饰器定义路由。定义完路由后,我们使用app.include_router()方法将路由器包含到主应用中。我们还可以为路由器指定前缀和标签,这样可以更好地组织API文档。
路由器的前缀是自动添加到所有路由路径的。例如,如果我们创建一个前缀为/api/v1的路由器,那么路由器中的所有路由都会自动添加这个前缀。这种设计使得版本控制变得简单,我们可以为不同版本的API创建不同的路由器。
让我们通过一个完整的例子来理解如何使用APIRouter组织路由。假设我们正在构建一个电商API,我们可以将路由分为用户、商品、订单等模块:
|# main.py - 主应用文件 from fastapi import FastAPI from routers import users, products, orders app = FastAPI(title="电商API", version="1.0.0") # 包含各个模块的路由器 app.include_router(users.router, prefix="/api/v1/users", tags=["用户"]) app.include_router(products.router, prefix="/api/v1/products", tags=[
|# routers/users.py - 用户模块 from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import List router = APIRouter() class User(BaseModel): id: int name: str email: str users_db = [] @router.get("/", response_model=
|# routers/products.py - 商品模块 from fastapi import APIRouter, Query from pydantic import BaseModel from typing import List, Optional router = APIRouter() class Product(BaseModel): id: int name: str price: float stock: int products_db = [] @router.get("/"
|# routers/orders.py - 订单模块 from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import List from datetime import datetime router = APIRouter() class OrderItem(BaseModel): product_id: int quantity: int class Order(BaseModel): id: int
这种组织方式使得代码结构清晰,每个模块都有明确的职责。当我们需要添加新功能时,只需要在相应的模块中添加路由即可。同时,这种模块化的设计也便于团队协作,不同的开发者可以负责不同的模块。
在RESTful API中,请求体通常用于传递复杂的数据结构。FastAPI对请求体的处理非常强大,它支持JSON、表单数据、文件上传等多种格式。理解FastAPI的请求体处理机制,可以帮助我们构建更加灵活的API。

JSON是Web API中最常用的数据格式,FastAPI对JSON请求体的处理非常完善。当我们定义一个接受请求体的端点时,我们只需要在函数参数中定义一个Pydantic模型,FastAPI会自动解析JSON请求体,并进行验证和转换。
Pydantic模型定义了请求体的结构。我们可以定义字段的类型、默认值、验证规则等。当请求到达时,FastAPI会使用Pydantic来解析和验证请求体。如果验证成功,数据会被转换为Pydantic模型实例,并传递给处理函数。如果验证失败,FastAPI会返回详细的错误信息,指出哪些字段有问题以及问题的原因。
这种自动解析和验证大大简化了我们的代码。我们不需要手动解析JSON,不需要手动验证数据,FastAPI会处理所有这些细节。我们只需要定义数据模型,FastAPI就会自动处理其余的工作。
请求体还可以是可选的。如果我们在函数参数中为Pydantic模型指定默认值None,并且类型是Optional[Model],那么请求体就是可选的。这在某些场景下很有用,比如更新操作,我们可能只想更新部分字段。
让我们通过实际例子来理解JSON请求体的处理。首先是一个基本的用户创建示例:
|from fastapi import FastAPI from pydantic import BaseModel, EmailStr, Field, validator from typing import Optional from datetime import datetime app = FastAPI() class UserCreate(BaseModel): """用户创建模型,定义请求体的结构""" name: str = Field(..., min_length=2, max_length=50,
对于嵌套的JSON结构,我们可以使用嵌套的Pydantic模型:
|class Address(BaseModel): """地址模型""" street: str city: str state: str zip_code: str class UserWithAddress(BaseModel): """包含地址的用户模型""" name: str email: EmailStr address: Address # 嵌套模型 @app.post("/users/with-address/", response_model=UserResponse) def create_user_with_address(user: UserWithAddress):
对于可选的请求体,我们可以这样处理:
|class UserUpdate(BaseModel): """用户更新模型,所有字段都是可选的""" name: Optional[str] = Field(None, min_length=2, max_length=50) email: Optional[EmailStr] = None age: Optional[int] = Field(None, ge=18, le=120) @app.patch(
对于列表类型的请求体,我们可以直接使用List:
|from typing import List class BulkUserCreate(BaseModel): """批量创建用户""" users: List[UserCreate] = Field(..., min_items=1, max_items=100) @app.post("/users/bulk/", response_model=List[UserResponse]) def create_users_bulk(bulk_data: BulkUserCreate): """批量创建用户""" created_users = [] for
这些例子展示了FastAPI处理JSON请求体的强大能力。我们只需要定义Pydantic模型,FastAPI就会自动处理JSON解析、类型验证、数据转换等所有细节。
除了JSON,FastAPI还支持表单数据。表单数据通常用于HTML表单提交,但在API中也有其用途。例如,文件上传通常使用表单数据格式。在FastAPI中,我们可以使用Form函数来定义表单字段。
表单字段的处理与查询参数类似,但有一些区别。表单字段的值是字符串类型,我们需要手动进行类型转换。FastAPI提供了Form()函数来帮助我们定义表单字段,它会自动进行类型转换和验证。
文件上传是表单数据的一个重要应用场景。在FastAPI中,我们可以使用File和UploadFile来处理文件上传。File用于单个文件,UploadFile提供了更多的功能,比如文件大小限制、文件类型验证等。FastAPI会自动处理文件的上传和存储,我们只需要定义处理逻辑。
让我们通过实际例子来理解表单数据的处理。首先是一个简单的表单提交示例:
|from fastapi import FastAPI, Form, File, UploadFile from typing import Optional, List app = FastAPI() @app.post("/login/") def login( username: str = Form(..., min_length=3, max_length=20), password: str = Form(..., min_length=
对于文件上传,我们可以这样处理:
|import shutil from pathlib import Path UPLOAD_DIR = Path("uploads") UPLOAD_DIR.mkdir(exist_ok=True) @app.post("/upload/") def upload_file(file: UploadFile = File(...)): """上传单个文件""" file_path = UPLOAD_DIR / file.filename with open(file_path, "wb"
我们还可以同时处理表单数据和文件上传:
|@app.post("/upload-with-metadata/") def upload_with_metadata( file: UploadFile = File(...), title: str = Form(...), description: Optional[str] = Form(None), tags: List[str] = Form([]) ): """上传文件并附带元数据""" file_path = UPLOAD_DIR / file.filename with
对于文件验证,我们可以添加更多的检查:
|from fastapi import HTTPException ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif"} MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB @app.post("/upload-image/") async def upload_image(file: UploadFile = File(...)): """上传图片文件,带验证""" # 检查文件扩展名 file_ext =
这些例子展示了FastAPI处理表单数据和文件上传的完整功能。FastAPI会自动处理表单解析、文件上传、类型转换等所有细节。
FastAPI使用Pydantic来进行请求体的验证和转换。Pydantic提供了强大的验证能力,包括类型验证、范围验证、格式验证等。我们可以使用Pydantic的验证器来定义自定义验证规则,这使得我们可以实现复杂的业务逻辑验证。
验证错误会返回详细的错误信息。FastAPI会自动生成错误响应,包括错误代码、错误消息、字段级别的错误信息等。这种详细的错误信息对于API的使用者非常有帮助,他们可以快速定位问题并修复。
数据转换是另一个重要的功能。Pydantic会自动进行类型转换,比如将字符串转换为整数、将ISO格式的字符串转换为日期时间对象等。这种自动转换使得我们可以直接使用转换后的数据,而不需要手动处理转换逻辑。
响应处理是API开发的另一个重要方面。FastAPI提供了灵活的响应处理机制,支持多种响应类型、状态码设置、响应头设置等。理解FastAPI的响应处理机制,可以帮助我们构建更加完善的API。
响应模型定义了API返回数据的结构。在FastAPI中,我们可以使用response_model参数来指定响应模型。响应模型可以是Pydantic模型,也可以是基本类型。FastAPI会自动将返回值序列化为响应模型定义的格式。
使用响应模型的好处是多方面的。首先,它提供了类型安全,确保返回的数据符合预期的结构。其次,它自动生成API文档,文档会显示响应数据的结构。最后,它提供了数据过滤功能,我们可以使用response_model_exclude参数来排除某些字段,这在某些场景下很有用,比如隐藏敏感信息。
响应模型还可以用于数据转换。我们可以定义一个响应模型,它只包含客户端需要的字段,FastAPI会自动从原始数据中提取这些字段。这种转换是自动的,我们不需要手动编写转换代码。
让我们通过实际例子来理解响应模型的使用。首先是一个基本的响应模型示例:
|from fastapi import FastAPI from pydantic import BaseModel, EmailStr from typing import Optional from datetime import datetime app = FastAPI() class UserBase(BaseModel): """用户基础模型""" name: str email: EmailStr class UserCreate(UserBase): """用户创建模型(包含密码)""" password: str
我们可以使用response_model_exclude来排除敏感字段:
|class UserFull(UserBase): """完整的用户模型""" id: int password: str # 敏感字段 created_at: datetime last_login: Optional[datetime] = None @app.get("/users/{user_id}", response_model=UserResponse) def get_user(user_id: int): """获取用户,自动排除密码字段""" user = next((u for u in
我们还可以使用response_model_include来只包含特定字段:
|@app.get("/users/{user_id}/summary", response_model=UserResponse, response_model_include={"id", "name"}) def get_user_summary(user_id: int): """获取用户摘要,只返回ID和姓名""" user = next((u for u in users_db if u["id"] == user_id), None) if not user:
对于嵌套的响应模型,我们可以这样处理:
|class AddressResponse(BaseModel): """地址响应模型""" street: str city: str zip_code: str class UserWithAddressResponse(UserResponse): """包含地址的用户响应""" address: AddressResponse @app.get("/users/{user_id}/with-address", response_model=UserWithAddressResponse) def get_user_with_address(user_id: int): """获取用户及其地址""" # 返回包含地址的用户数据
这些例子展示了响应模型的强大功能。通过定义响应模型,我们可以确保API返回的数据结构一致,同时自动过滤敏感信息,并生成准确的API文档。
HTTP状态码是API响应的重要组成部分,它告诉客户端请求的处理结果。FastAPI允许我们通过status_code参数来设置响应状态码。我们可以使用标准的HTTP状态码,比如200表示成功,201表示创建成功,404表示资源不存在等。
状态码的选择应该符合HTTP标准和RESTful设计原则。GET请求成功应该返回200,POST请求创建资源成功应该返回201,PUT请求更新资源成功应该返回200或204,DELETE请求成功应该返回204。错误情况应该返回相应的4xx或5xx状态码。
FastAPI还提供了status模块,其中定义了常用的状态码常量。使用这些常量可以使代码更加清晰,避免硬编码数字。例如,我们可以使用status.HTTP_200_OK而不是直接使用200。
响应头可以包含额外的信息,比如缓存控制、内容类型、自定义头部等。在FastAPI中,我们可以使用Response对象来设置响应头。我们可以创建Response对象,设置头部,然后返回它。
FastAPI还支持通过依赖注入来设置响应头。我们可以创建一个依赖函数,设置响应头,然后在路由中使用这个依赖。这种方式使得响应头的设置更加模块化和可重用。
自定义响应头在某些场景下很有用,比如API版本信息、请求ID、速率限制信息等。这些信息可以帮助客户端更好地理解响应,或者用于调试和监控。
FastAPI使用Pydantic来进行响应序列化。当处理函数返回数据时,FastAPI会使用响应模型(如果指定了)来序列化数据。序列化过程包括类型转换、数据验证、格式转换等。
对于基本类型,序列化是直接的。对于复杂类型,比如Pydantic模型,FastAPI会调用模型的序列化方法。我们可以自定义序列化方法,实现自定义的序列化逻辑。
JSON序列化是默认的,但FastAPI也支持其他格式,比如XML、YAML等。我们可以通过设置响应头来指定内容类型,FastAPI会根据内容类型选择合适的序列化方式。

异步编程是FastAPI的重要特性之一,它使得FastAPI能够高效地处理大量并发请求。理解异步编程的原理和使用方法,对于构建高性能的FastAPI应用至关重要。
异步编程是一种编程范式,它允许程序在等待I/O操作时执行其他任务,而不是阻塞等待。在传统的同步编程中,当一个操作需要等待I/O时,程序会阻塞,直到I/O操作完成。在异步编程中,程序可以继续执行其他任务,当I/O操作完成时,程序会收到通知并继续处理。
Python的异步编程基于async/await语法。async关键字用于定义异步函数,await关键字用于等待异步操作完成。异步函数返回协程对象,而不是直接返回结果。我们需要在异步上下文中运行协程,才能获得结果。
在FastAPI中,我们可以定义异步处理函数。当FastAPI检测到函数是异步的,它会使用异步方式调用函数。这意味着函数可以执行异步操作,比如数据库查询、HTTP请求等,而不会阻塞其他请求的处理。
在FastAPI中定义异步函数很简单,我们只需要在函数定义前添加async关键字。例如,我们可以定义一个异步函数来处理用户创建请求:
|@app.post("/users/") async def create_user(user: UserCreate): # 异步操作 result = await some_async_operation(user) return result
在这个例子中,create_user是一个异步函数,它可以执行异步操作。当FastAPI调用这个函数时,它会使用异步方式执行,不会阻塞其他请求。
异步函数可以调用其他异步函数,使用await关键字等待结果。我们也可以调用同步函数,但应该避免在异步函数中执行耗时的同步操作,因为这会影响异步的性能优势。
数据库操作是异步编程的典型应用场景。传统的同步数据库操作会阻塞线程,直到数据库响应。在异步编程中,我们可以使用异步数据库驱动,比如asyncpg(PostgreSQL)、aiomysql(MySQL)等,这些驱动支持异步操作,不会阻塞线程。
使用异步数据库操作时,我们需要使用await关键字等待数据库响应。在等待期间,FastAPI可以处理其他请求,这使得应用可以处理更多的并发请求。这种并发能力的提升对于高负载应用非常重要。
SQLAlchemy也支持异步操作。我们可以使用SQLAlchemy的异步API来执行数据库操作。异步SQLAlchemy的使用方式与同步版本类似,但需要使用异步上下文管理器和异步会话。
除了数据库操作,HTTP请求也是异步编程的典型应用场景。当我们需要在FastAPI应用中调用其他API时,我们应该使用异步HTTP客户端,比如httpx。异步HTTP客户端不会阻塞线程,可以同时处理多个HTTP请求。
使用httpx进行异步HTTP请求很简单。我们创建httpx.AsyncClient实例,然后使用await关键字调用请求方法。httpx会自动处理连接池、重试、超时等细节,使得HTTP请求变得简单可靠。
异步HTTP客户端特别适合调用多个外部API的场景。我们可以并发地发起多个请求,等待所有请求完成,然后处理结果。这种并发处理可以显著减少总的等待时间,提高应用的响应速度。
虽然异步编程有很多优势,但也有一些注意事项。首先,不是所有操作都适合异步。CPU密集型操作不适合异步,因为Python的GIL会限制多线程的性能。对于CPU密集型操作,我们应该使用多进程或专门的库。
其次,异步编程需要异步库的支持。如果我们使用的库不支持异步,我们可能需要使用线程池来执行同步操作。虽然这可以工作,但会失去异步的性能优势。我们应该尽量使用异步库,以获得最佳性能。
最后,异步编程的调试可能比同步编程更复杂。异步代码的执行顺序可能不是线性的,错误堆栈可能更难理解。我们需要使用专门的工具和技术来调试异步代码。
FastAPI的一个突出特性是自动生成API文档。这个特性大大简化了API文档的维护工作,确保文档总是与代码保持同步。理解FastAPI的文档生成机制,可以帮助我们更好地利用这个特性。
FastAPI的文档生成基于OpenAPI规范(原Swagger规范)。OpenAPI是一个标准的API描述格式,它定义了API的结构、端点、参数、响应等。FastAPI会自动从代码中提取信息,生成OpenAPI规范文档。
OpenAPI规范是机器可读的,这意味着工具可以自动处理它。我们可以使用各种工具来查看、测试、生成客户端代码等。这种标准化使得FastAPI生成的API更容易被其他系统集成。
FastAPI生成的OpenAPI文档包括所有端点的信息,包括路径、方法、参数、请求体、响应等。文档还包含类型信息、验证规则、示例值等,这些信息对于API的使用者非常有帮助。
Swagger UI是FastAPI默认提供的文档界面。我们可以通过访问/docs路径来查看Swagger UI。Swagger UI提供了一个交互式的界面,我们可以在其中查看API文档、测试API端点、查看请求和响应示例等。
Swagger UI的界面非常友好,它使用颜色编码来区分不同的HTTP方法,使用折叠面板来组织信息,使用表单来输入参数。我们可以直接在界面中测试API,不需要使用外部工具。这种交互式体验大大提升了API的使用体验。
Swagger UI还支持认证。如果我们的API需要认证,我们可以在Swagger UI中配置认证信息,然后所有的API请求都会自动包含认证信息。这种集成使得测试受保护的API变得简单。
除了Swagger UI,FastAPI还提供了ReDoc界面。我们可以通过访问/redoc路径来查看ReDoc。ReDoc提供了另一种文档查看方式,它的界面更加简洁,专注于文档的阅读。
ReDoc的界面设计更加传统,类似于传统的API文档。它使用侧边栏来导航,使用主区域来显示详细信息。ReDoc的界面更加适合阅读,而Swagger UI更加适合交互式测试。
选择使用哪个界面主要取决于个人偏好和使用场景。如果我们需要交互式测试,Swagger UI可能更合适。如果我们只需要阅读文档,ReDoc可能更合适。FastAPI同时提供两个界面,我们可以根据需要选择使用。
虽然FastAPI可以自动生成文档,但我们也可以自定义文档的内容。我们可以设置API的标题、描述、版本等信息,这些信息会显示在文档的顶部。我们还可以为每个端点添加描述和摘要,这些信息会显示在文档中。
文档字符串是自定义文档的重要方式。我们可以在处理函数中添加文档字符串,FastAPI会自动提取这些字符串并显示在文档中。文档字符串应该清晰地描述端点的用途、参数的含义、返回值的格式等。
我们还可以使用response_description参数来添加响应描述,使用summary参数来添加端点摘要,使用tags参数来组织端点。这些参数使得文档更加丰富和有用。
生成的API文档可以轻松地部署和分享。由于文档是自动生成的,我们不需要单独维护文档文件。我们只需要确保API代码是最新的,文档就会自动更新。
我们可以将文档部署到任何支持静态文件的服务上。OpenAPI规范是JSON格式的,我们可以将其导出并部署。Swagger UI和ReDoc也可以独立部署,只需要提供OpenAPI规范文件即可。
对于内部团队,我们可以直接在开发服务器上访问文档。对于外部用户,我们可以将文档部署到公共服务器上,或者集成到API网关中。这种灵活性使得文档的分享变得简单。

数据验证是API开发中的重要环节,它确保输入数据的正确性和安全性。FastAPI使用Pydantic来进行数据验证,这提供了强大而灵活的验证能力。理解FastAPI的数据验证机制,可以帮助我们构建更加健壮的API。
Pydantic模型是FastAPI数据验证的核心。Pydantic模型定义了数据的结构、类型、验证规则等。当我们定义一个Pydantic模型时,我们实际上定义了一个数据契约,这个契约规定了数据必须满足的条件。
Pydantic模型使用Python的类型提示来定义字段类型。我们可以使用基本类型,比如int、str、bool等,也可以使用复杂类型,比如List、Dict、Optional等。Pydantic会根据类型提示自动进行类型验证和转换。
字段还可以有默认值。如果字段有默认值,它就是可选的;如果没有默认值,它就是必需的。这种设计使得模型定义既灵活又严格,我们可以根据需要定义不同的验证规则。
Pydantic提供了丰富的字段验证规则。我们可以使用Field函数来定义验证规则,比如最小值、最大值、长度限制、正则表达式等。这些验证规则会在数据验证时自动执行,如果验证失败,Pydantic会抛出验证错误。
例如,我们可以定义一个用户模型,要求用户名长度在3到20个字符之间,邮箱必须符合邮箱格式,年龄必须在18到120之间。这些验证规则可以确保数据的正确性,防止无效数据进入系统。
自定义验证器是另一个强大的功能。我们可以定义函数来执行自定义验证逻辑。自定义验证器可以访问字段的值,可以访问其他字段的值,可以实现复杂的业务逻辑验证。这种灵活性使得我们可以实现任何需要的验证规则。
Pydantic不仅验证数据,还转换数据。当数据通过验证后,Pydantic会将数据转换为模型实例。转换过程包括类型转换、格式转换等。例如,Pydantic可以将字符串"123"转换为整数123,可以将ISO格式的字符串转换为日期时间对象。
数据转换是自动的,我们不需要手动处理。这种自动转换使得我们可以直接使用转换后的数据,而不需要编写大量的转换代码。转换过程是类型安全的,Pydantic会确保转换后的数据符合类型要求。
转换还可以处理嵌套结构。如果模型包含嵌套模型,Pydantic会递归地转换嵌套数据。这种递归转换使得我们可以处理复杂的数据结构,而不需要手动处理每一层。
当数据验证失败时,Pydantic会生成详细的错误信息。错误信息包括字段名称、错误类型、错误消息等。FastAPI会自动将这些错误信息格式化为HTTP响应,返回给客户端。
错误响应的格式是标准的,符合HTTP和JSON规范。客户端可以解析错误响应,提取错误信息,并向用户显示友好的错误消息。这种标准化的错误格式使得错误处理变得一致和可预测。
我们还可以自定义错误处理。FastAPI允许我们定义自定义异常处理器,我们可以捕获特定的异常,返回自定义的错误响应。这种自定义能力使得我们可以实现符合业务需求的错误处理逻辑。
错误处理是API开发中的重要方面。良好的错误处理可以提供清晰的错误信息,帮助客户端理解问题并采取适当的行动。FastAPI提供了强大的错误处理机制,包括自动错误处理、自定义异常、错误响应格式等。
在Python中,异常是错误处理的标准机制。当代码执行过程中发生错误时,Python会抛出异常。如果异常没有被捕获,程序会终止。在Web应用中,我们需要捕获异常,并返回适当的HTTP错误响应。
FastAPI会自动捕获某些类型的异常,并返回适当的错误响应。例如,如果路由处理函数抛出HTTPException,FastAPI会捕获它并返回相应的HTTP错误响应。这种自动处理简化了错误处理代码。
我们也可以使用try-except语句来手动捕获异常。在捕获异常后,我们可以记录错误信息,执行清理操作,然后返回适当的错误响应。这种手动处理提供了更多的控制,但也需要更多的代码。
HTTPException是FastAPI提供的异常类,用于表示HTTP错误。我们可以抛出HTTPException来返回错误响应。HTTPException接受状态码、错误消息、错误详情等参数,我们可以根据需要设置这些参数。
使用HTTPException很简单。在路由处理函数中,我们可以检查条件,如果条件不满足,就抛出HTTPException。FastAPI会自动捕获这个异常,并返回相应的错误响应。这种机制使得错误处理变得简单和一致。
HTTPException还可以包含错误详情。我们可以传递一个字典,包含更详细的错误信息。这些详情会包含在错误响应中,帮助客户端更好地理解错误。例如,我们可以包含字段级别的错误信息,指出哪些字段有问题以及问题的原因。
让我们通过实际例子来理解HTTPException的使用:
|from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float items_db = {} @app.get("/items/{item_id}") def get_item(item_id: int): """获取物品,如果不存在则返回404""" if item_id
对于需要验证的场景,我们可以提供更详细的错误信息:
|@app.post("/items/validate/") def create_item_with_validation(item: Item): """创建物品,带业务逻辑验证""" errors = [] # 验证价格 if item.price < 0: errors.append({"field": "price", "message": "价格不能为负数"}) # 验证名称 if len(item.name) < 3: errors.append({"field":
对于权限验证,我们可以这样处理:
|@app.get("/items/{item_id}/admin") def get_item_admin(item_id: int, is_admin: bool = False): """管理员获取物品详情""" if not is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="需要管理员权限" ) if item_id not in items_db: raise HTTPException(
这些例子展示了HTTPException的各种用法。通过使用HTTPException,我们可以提供清晰的错误信息,帮助客户端理解问题并采取适当的行动。
虽然HTTPException可以处理大多数情况,但有时我们需要更复杂的错误处理逻辑。FastAPI允许我们定义自定义异常处理器,我们可以捕获特定的异常类型,并返回自定义的错误响应。
自定义异常处理器的定义使用@app.exception_handler()装饰器。我们指定要捕获的异常类型,然后定义处理函数。处理函数接收请求和异常对象,返回错误响应。这种机制使得我们可以实现符合业务需求的错误处理逻辑。
自定义异常处理器可以处理应用特定的异常。例如,我们可以定义业务逻辑异常,比如UserNotFoundError、InvalidOperationError等,然后为这些异常定义专门的处理器。这种设计使得错误处理更加清晰和可维护。
除了路由级别的错误处理,我们还可以定义全局错误处理器。全局错误处理器可以捕获所有未被处理的异常,确保应用不会因为未处理的异常而崩溃。这对于生产环境特别重要,我们可以记录错误信息,返回友好的错误响应,而不是暴露内部错误。
全局错误处理器通常用于处理意外的异常,比如编程错误、系统错误等。我们可以记录详细的错误信息,包括堆栈跟踪,这对于调试很有帮助。同时,我们返回通用的错误响应,不暴露敏感信息。
错误日志记录是全局错误处理的重要组成部分。我们应该记录所有错误,包括错误类型、错误消息、堆栈跟踪、请求信息等。这些日志可以帮助我们诊断问题,改进应用。我们可以使用Python的logging模块来记录错误,或者使用专门的日志服务。
中间件是FastAPI的另一个重要特性,它允许我们在请求处理过程中插入自定义逻辑。中间件可以用于日志记录、认证、CORS处理、请求转换等。理解FastAPI的中间件机制,可以帮助我们实现跨切面的功能。

中间件是请求处理管道中的组件,它可以在请求到达处理函数之前或响应返回客户端之后执行代码。中间件可以访问请求和响应对象,可以修改请求或响应,可以终止请求处理等。这种机制使得我们可以实现横切关注点,而不需要在每个路由处理函数中重复代码。
FastAPI的中间件基于Starlette的中间件系统。Starlette提供了ASGI中间件接口,FastAPI在此基础上提供了更高级的抽象。我们可以使用装饰器来定义中间件,这使得中间件的使用变得简单。
中间件的执行顺序很重要。中间件按照添加的顺序执行,请求时从第一个中间件开始,响应时从最后一个中间件开始。这种顺序性使得我们可以控制中间件的执行顺序,实现复杂的处理逻辑。
FastAPI提供了一些内置中间件,比如CORS中间件。CORS(Cross-Origin Resource Sharing)是Web安全的重要机制,它控制哪些源可以访问API。在开发中,我们经常需要处理CORS问题,FastAPI的CORS中间件可以简化这个过程。
使用CORS中间件很简单,我们只需要创建CORSMiddleware实例,并配置允许的源、方法、头部等。FastAPI会自动处理CORS相关的HTTP头部,使得跨域请求变得简单。这种内置支持大大简化了CORS的处理。
除了CORS中间件,FastAPI还支持其他中间件,比如信任代理中间件、GZip压缩中间件等。这些中间件可以处理常见的Web应用需求,减少我们需要编写的代码。
虽然FastAPI提供了内置中间件,但有时我们需要实现自定义中间件。自定义中间件可以实现特定的业务逻辑,比如请求日志记录、性能监控、请求转换等。
实现自定义中间件需要实现ASGI接口。ASGI接口定义了三个参数:scope、receive、send。scope包含请求的信息,receive用于接收请求体,send用于发送响应。实现ASGI接口需要理解异步编程,但FastAPI提供了更简单的抽象。
我们可以使用函数来定义中间件,函数接收请求和调用下一个中间件的函数。在函数中,我们可以处理请求,调用下一个中间件,处理响应。这种函数式的方式使得中间件的实现变得简单和直观。
中间件有很多应用场景。日志记录是常见的应用场景,我们可以在中间件中记录请求和响应信息,包括路径、方法、状态码、处理时间等。这些日志对于监控和调试很有帮助。
认证和授权也可以使用中间件实现。我们可以在中间件中检查认证信息,验证权限,如果认证或授权失败,可以提前返回错误响应。这种集中式的认证和授权使得代码更加清晰和可维护。
性能监控是另一个应用场景。我们可以在中间件中记录请求处理时间,统计请求数量,监控错误率等。这些指标可以帮助我们了解应用的性能,发现性能瓶颈。
这一节课,我们一起深入探索了FastAPI的核心特性。从路由系统、请求和响应处理,再到异步编程、自动文档生成、数据验证、错误处理以及中间件,相信你已经能体会到FastAPI带来的高效与灵活。
接下来,我们将走进依赖注入系统的世界。依赖注入是FastAPI非常独特且实用的能力,学会它可以让你的代码更加模块化、易维护,也更方便测试!