FastAPI系列03:FastAPI路由(router)


在“FastAPI系列02:FastAPI程序结构与生命周期”一节中,我们以宏观的视野分析了FastAPI框架的结构,了解了FastAPI路由系统在FastAPI框架中的重要地位,本节将对FastAPI路由进行展开分析,进一步了解FastAPI的路由规则、路由管理器以及路由的注册和执行过程。

1、路由的注册过程

路由是 Web 应用中的 URL 映射规则。FastAPI 使用装饰器的方式将这些URL 映射规则与指定的路由函数绑定起来,从而完成特定的 HTTP 请求与具体Python 函数响应的连接。
FastAPI 提供了以下几种方式向路由管理器注册路由:

1)使用 FastAPI 实例注册路由

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}
    

说明:
• 使用 @app.get(“/items/{item_id}”) 注册路由
• item_id 是路径参数
• q 是查询参数,默认为 None

2)使用 APIRouter 模块化路由

APIRouter 提供了更好的路由管理方式,便于拆分和复用。

from fastapi import FastAPI, APIRouter

app = FastAPI()
router = APIRouter()

@router.get("/users/{user_id}")
async def read_user(user_id: int):
    return {"user_id": user_id}

# 将路由挂载到主应用
app.include_router(router, prefix="/api", tags=["Users"])

说明:
• 使用 APIRouter() 创建路由对象
• 使用 include_router() 将路由挂载到主应用
• prefix=“/api” 为路由统一加前缀 /api

3)路由注册装饰器方法

FastAPI 提供了简洁的装饰器方法来定义路由。常用的路由方法包括:
@app.get():处理 GET 请求
@app.post():处理 POST 请求
@app.put():处理 PUT 请求
@app.delete():处理 DELETE 请求
@app.patch():处理 PATCH 请求
@app.options():处理 OPTIONS 请求
@app.head():处理 HEAD 请求

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"message": "Hello, FastAPI"}

@app.post("/items/")
async def create_item(name: str):
    return {"name": name}

2、路由管理器

在FastAPI中,所有的注册路由都会统一保存到app.routes中,app.routes保存了所有的路由注册信息,包括通过API Router所注册的路由。

使用 app.routes 查看所有路由

from fastapi import FastAPI, APIRouter
app = FastAPI()

# 由FastAPI 实例注册的路由
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

@app.post("/items/")
async def create_item(name: str):
    return {"name": name}

# 由APIRouter 注册的路由 
router = APIRouter()
@router.get("/users/")
async def get_users():
    return [{"name": "Alice"}, {"name": "Bob"}]

app.include_router(router)

# 打印所有路由
for route in app.routes:
    print(f"Path: {route.path}, Methods: {route.methods}")

如果需要更多的路由信息,比如处理函数的名称、依赖项等,可以通过以下方式获取:

for route in app.routes:
    print(f"Path: {route.path}")
    print(f"Methods: {route.methods}")
    print(f"Name: {route.name}")
    print(f"Endpoint: {route.endpoint}")
    print("-" * 30)

说明:
• route.path: 路径
• route.methods: 支持的 HTTP 方法
• route.name: 路由的名称
• route.endpoint: 对应的 Python 处理函数

3、路由规则

1)路由规则的结构

在 FastAPI 中,一个路由规则主要由以下组件构成:

组件说明示例
路径 (Path)URL 中的访问路径/items/{item_id}
方法 (Method)HTTP 请求方法如 GET、POST 等 GET、POST
操作处理函数处理该路径和方法的函数async def read_item()
路径参数动态参数,用于捕获路径中的变量{item_id}
查询参数使用 ?key=value 形式的参数?q=search
请求体参数用于接收 JSON 等格式的数据{ “name”: “item1”, “price”: 10.0 }
依赖项 (Dependency)依赖注入,用于共享逻辑或中间处理Depends(get_db)
响应模型定义 API 响应的数据结构ResponseModel
状态码定义成功或失败时的 HTTP 状态码200 OK、404 Not Found

2)路由规则的配置

使用路由方法注册路由时可以配置相关的参数,下面是@app.get()方法的参数配置说明,其他方法也类似:

@app.get(
    # 【基本参数】
    "/items/{item_id}",                             # path: 定义路径,支持路径参数 {item_id}
    status_code=200,                                # status_code: 指定成功时的 HTTP 状态码
    # 【response 参数】
    response_model=Item,                            # response_model: 指定响应数据模型,用于数据校验和文档生成
    response_class=None,                            # response_class: 自定义响应类型,例如 JSONResponse 或 HTMLResponse
    responses={404: {"description": "Item not found"}}, # responses: 自定义特定状态码的响应信息
    # 【依赖参数】
    dependencies=[Depends(get_current_user)],       # dependencies: 定义依赖项,常用于认证、权限管理、日志记录等
    # 【OpenAPI 参数】
    tags=["items"],                                 # tags: 为路由分组打标签,方便在 API 文档中展示
    summary="获取指定 ID 的商品信息",                 # summary: 简要描述接口功能
    description="根据商品 ID 获取商品信息,支持可选查询参数进行过滤", # description: 详细描述接口功能,支持 Markdown 格式
    deprecated=False,                               # deprecated: 标记该接口是否已废弃
    operation_id="read_item_by_id",                 # operation_id: 自定义操作 ID,用于在 OpenAPI 文档中唯一标识此接口
    include_in_schema=True                          # include_in_schema: 是否在 OpenAPI 文档中显示该路由,False 隐藏

)

基本参数

参数名作用示例
path定义 API 路径,支持路径参数/items/{item_id}
status_code设置成功时的 HTTP状态码 200

依赖参数

参数名作用示例
dependencies定义依赖项,常用于认证、权限管理、日志记录等[Depends(get_current_user)]

Response 参数

参数名作用示例
response_model指定响应的数据模型,确保输出符合预期Item
response_class自定义响应类,例如 JSONResponse、HTMLResponse JSONResponse
responses自定义状态码的响应信息和描述{404: {“description”: “Item not found”}}

OpenAPI 参数

参数名作用示例
tags分配标签,用于在 API 文档中分组显示[‘items’]
summary简要描述 API 的功能“获取指定 ID 的商品信息”
description详细描述 API 的功能,支持 Markdown“根据商品 ID 获取商品信息”
deprecated标记 API 是否已废弃,True 表示已废弃False
operation_id自定义操作 ID,用于 OpenAPI 中标识接口“read_item_by_id”
include_in_schema控制 API 是否在 OpenAPI 文档中显示True

3)路径参数、查询参数、请求体参数

FastAPI 中,路由处理函数可以接收 路径参数查询参数请求体参数,它们各自有不同的作用和使用场景。
路径参数(Path Parameters)
路径参数是在 URL 的路径中定义的参数,用于标识特定的资源,路径参数可以清晰地表示资源之间的层级关系。
• 使用 {} 包裹路径参数,并在函数中声明对应的参数名。
• 参数类型需要在函数签名中明确标注。

from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

说明
• /items/{item_id}:定义了 item_id 作为路径参数。
• item_id: int:在函数中定义并指定数据类型,FastAPI会自动校验。
• 响应:返回 item_id 的值。
注意事项
• 路径参数是必需的。
• 数据类型必须匹配,否则会返回 422 Unprocessable Entity
• 路径参数数据类型可以是基本类型,也可以是枚举类型。
查询参数(Query Parameters)
查询参数是在 URL 中 ?key=value 形式的参数,通常用于对资源进行筛选、排序、分页等操作,适合在需要对资源进行动态查询时使用。
• 在函数中直接声明参数即可,FastAPI 会自动识别它们为查询参数。

from typing import Optional
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(q: Optional[str] = None, limit: int = 10):
    return {"query": q, "limit": limit}

说明
• /items/?q=book&limit=5:传入查询参数 q 和 limit。
• Optional[str] = None:q 是可选参数,默认为 None。
• limit: int = 10:limit 参数有默认值 10。
• 响应:返回传入的查询参数。
注意事项
• 查询参数是可选的,但可以设置默认值。
• 支持多参数使用 & 连接:/items/?q=book&limit=5。

请求体参数(Request Body)
请求体参数通过在 HTTP 请求的 Body 中发送数据,用于传递大量的数据或复杂的数据结构,适合在创建、更新资源时使用。
• FastAPI 使用 Pydantic 模型来定义请求体,并自动进行数据验证。

from pydantic import BaseModel
from fastapi import FastAPI

app = FastAPI()

# 定义请求体模型
class Item(BaseModel):
    name: str
    description: str
    price: float
    tax: Optional[float] = None

@app.post("/items/")
async def create_item(item: Item):
    return {"item_name": item.name, "item_price": item.price}

说明
• Item:使用 Pydantic 定义请求体数据模型。
• item: Item:FastAPI 自动将请求体数据映射到 Item 实例。
• 响应:返回处理后的数据。
注意事项
• 请求体数据必须是合法的 JSON 格式。
• FastAPI 会根据 BaseModel 自动校验数据类型和必填字段。
• 请求体一般用于数据创建、更新等场景。

4、路由函数

在 FastAPI中,路由函数 是用来处理客户端请求并返回响应的函数。它是一个在特定的 URL 路径和 HTTP 方法(如 GET、POST、PUT、DELETE 等)上执行的函数。

1)路由函数参数

在 FastAPI中,路由函数一般以路径参数、查询参数、请求体参数为函数参数。在前面的例子中我们已经详细解析了路径参数、查询参数、请求体参数的使用,在此不再赘述。

1)同步、异步

在 FastAPI 中,路由函数可以是同步函数或异步函数,这两种类型的路由函数在使用和性能表现上有所不同,下面为你详细介绍:

同步路由函数
同步函数 是常规的阻塞式函数,执行完成后才会返回结果。适用于 CPU 密集型任务或不涉及 I/O 操作的场景

from fastapi import FastAPI

app = FastAPI()

@app.get("/sync")
def sync_function():
    # 模拟CPU密集型任务
    result = sum(i for i in range(1000000))
    return {"result": result}

说明
• def sync_function():使用普通的同步函数。
适用场景:数学计算、数据处理等。
阻塞行为:函数执行期间,无法处理其他请求。

异步路由函数
异步函数 使用 async def 定义,通常搭配 await 调用异步 I/O 操作。适用于 I/O 密集型任务,例如数据库查询、网络请求等。FastAPI 使用 Python 的 asyncio 库实现异步操作。

import asyncio
from fastapi import FastAPI

app = FastAPI()

@app.get("/async")
async def async_function():
    # 模拟异步I/O操作
    await asyncio.sleep(2)
    return {"message": "Completed after 2 seconds"}

说明
async def async_function():定义异步函数。
await asyncio.sleep(2):模拟一个耗时 2 秒的异步操作。
适用场景:I/O 密集型任务如数据库操作、调用外部 API 等,高并发场景。
非阻塞行为:在等待期间可以处理其他请求。

5、路由的执行过程

1)FastAPI路由的执行过程

当一个请求发送到 FastAPI 时,执行过程如下:

路由匹配
路径参数解析
依赖注入
请求体和查询参数解析
执行路由函数
生成响应

Step 1:路由匹配

  1. FastAPI 使用基于 ASGI 的路由树结构,遍历所有注册的路由。
  2. 根据请求的 PathMethod 进行匹配。
  3. 如果匹配成功,进入下一步;否则返回 404 Not Found。

Step 2:路径参数解析
• FastAPI 会从 URL 中提取路径参数,并进行类型转换。
• 如果参数不符合定义的类型(如 int),返回 422 Unprocessable Entity。

Step 3:依赖注入
• FastAPI 执行 Depends() 中定义的依赖项。
• 如果依赖项执行失败,会返回 HTTPException 的错误信息。

Step 4:请求体和查询参数解析
• FastAPI 会根据函数签名中的参数,从请求体或查询参数中提取数据。
• 自动进行数据校验和类型转换。

Step 5:执行路由函数
• FastAPI 调用目标函数,将解析后的参数传入执行。
• 如果函数是异步的 (async),FastAPI 使用 asyncio 进行调度。

Step 6:生成响应
• FastAPI 会根据函数的返回值和响应模型生成 HTTP 响应。
• 默认返回 200 OK,除非另有指定。

2)路由规则的匹配优先级

先定义的路由优先匹配
FastAPI 中,先定义的路由优先匹配,如果有多个路由能匹配某个请求路径,FastAPI 会选择第一个符合条件的路由。

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"message": "This is a generic item"}

@app.get("/items/special")
async def read_special_item():
    return {"message": "This is a special item"}

结果:
• GET /items/special:返回 {“message”: “This is a generic item”}
• 即使有精确匹配的路由,FastAPI 已经在上一个路由中匹配到了 /items/{item_id}。

静态路由优先于动态路由
如果 FastAPI 能找到完全匹配的静态路径,就不会再考虑其他路由。

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/me")
async def read_current_user():
    return {"user": "current user"}

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return {"user_id": user_id}

结果:
• GET /users/me:返回 {“user”: “current user”}
• GET /users/123:返回 {“user_id”: 123}
即使 /users/{user_id} 也能匹配 /users/me**,但静态路由优先。**

更具体的参数类型优先
如果两个路由路径都包含变量,FastAPI 会根据参数类型的精确程度决定优先级,例如 int 优先于 str。

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item_int(item_id: int):
    return {"item_id": item_id, "type": "int"}

@app.get("/items/{item_id}")
async def read_item_str(item_id: str):
    return {"item_id": item_id, "type": "str"}

结果:
• GET /items/123:返回 {“item_id”: 123, “type”: “int”}
• GET /items/abc:返回 {“item_id”: “abc”, “type”: “str”}

2)路由规则的覆盖规则

FastAPI 中,如果出现 重复的路由规则(即路径和方法相同),会产生以下几种情况:
1.路径和方法完全相同的重复路由,会覆盖:FastAPI 会直接覆盖之前的路由,最后注册的路由会生效。
2.路径和方法相同但参数不同,不会覆盖:FastAPI 会根据参数的类型和名称区分路由,通常不会报错。
3.路径相同但HTTP方法不同,不会覆盖:FastAPI 允许同一路径使用不同的 HTTP 方法,例如同一个路径同时支持 GET 和 POST 请求。请求时方法完全匹配的路由会被优先选择。

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id, "method": "GET"}

@app.post("/items/{item_id}")
async def create_item(item_id: int):
    return {"item_id": item_id, "method": "POST"}

结果:
• GET /items/123:返回 {“item_id”: 123, “method”: “GET”}
• POST /items/123:返回 {“item_id”: 123, “method”: “POST”}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值