在使用FastAPI开发接口时,路径参数(Path Parameters)是一种非常灵活的传参方式,比如/users/{user_id}中的user_id。但FastAPI的路径参数远不止基础用法,它们支持动态路由和参数校验,让接口更灵活、更健壮。
一、回顾基础路径参数¶
首先,我们先快速回顾一下路径参数的基础用法。比如,定义一个根据用户ID返回用户信息的接口:
from fastapi import FastAPI
app = FastAPI()
# 基础路径参数:user_id是路径参数,默认类型为str
@app.get("/users/{user_id}")
async def get_user(user_id: str):
return {"user_id": user_id, "message": f"获取用户 {user_id} 的信息"}
当访问/users/123时,user_id会被自动识别为字符串"123"。但FastAPI允许我们显式指定参数类型(如int、float),让参数自动转换和校验。
二、动态路由进阶:让路径参数更灵活¶
动态路由指的是路径参数可以根据实际需求“动态变化”,支持多种参数组合或特殊格式。
1. 路径参数类型自动转换¶
FastAPI会自动尝试将路径参数转换为指定类型(如int、float)。例如,即使URL中传的是字符串"123",只要我们定义user_id: int,FastAPI会自动将其转为整数:
@app.get("/users/{user_id}")
async def get_user(user_id: int): # 自动转换为int类型
return {"user_id": user_id, "type": type(user_id)} # 输出:{"user_id": 123, "type": <class 'int'>}
如果参数类型转换失败(如/users/abc中传"abc"给int类型的user_id),FastAPI会直接返回422 Validation Error,避免非法数据进入接口。
2. 可选路径参数¶
有时路径参数可能不是必须的,比如/items/{item_id}/details/{sub_id},其中sub_id可能是可选的。这时可以用Optional类型+默认值:
from typing import Optional
@app.get("/items/{item_id}/details/{sub_id}")
async def get_item_details(
item_id: int, # 必须参数
sub_id: Optional[int] = None # 可选参数,默认None
):
result = {"item_id": item_id}
if sub_id:
result["sub_id"] = sub_id
return result
访问/items/123/details/456会返回{"item_id": 123, "sub_id": 456},而访问/items/123(不带sub_id)会返回{"item_id": 123}。
3. 路径参数的正则表达式限制¶
如果需要严格限制路径参数的格式(如只能是字母、数字组合),可以用Path的pattern参数配合正则表达式:
from fastapi import Path
@app.get("/orders/{order_code}")
async def get_order(order_code: str = Path(..., pattern="^[A-Z0-9]{8}$")):
# order_code必须是8位大写字母或数字(如"ABC12345")
return {"order_code": order_code}
这里pattern="^[A-Z0-9]{8}$"表示:
- ^和$:匹配字符串开头和结尾
- [A-Z0-9]:允许大写字母或数字
- {8}:必须连续出现8次
三、参数校验进阶:让接口更安全¶
光有动态路由还不够,FastAPI支持对路径参数进行严格校验(如范围、枚举值等),确保传入的数据合法。
1. 用Path限制参数范围¶
使用Path函数可以给路径参数设置最小值、最大值、是否必须等规则。例如,定义商品ID必须在1到100之间:
@app.get("/products/{item_id}")
async def get_product(
item_id: int = Path(
..., # 必传参数(...表示必填)
ge=1, # ge=1:大于等于1
le=100, # le=100:小于等于100
description="商品ID必须是1-100之间的整数"
)
):
return {"item_id": item_id, "message": f"获取商品 {item_id} 的详情"}
ge(greater than or equal):最小值le(less than or equal):最大值- 若传入不合法参数(如
/products/0或/products/101),FastAPI会直接返回422错误,并提示“商品ID必须是1-100之间的整数”。
2. 枚举类型路径参数¶
如果参数只能是特定枚举值(如商品分类只能是"book"、"electronics"、"clothes"),可以用Enum或Literal实现:
from enum import Enum
class Category(str, Enum):
BOOK = "book"
ELECTRONICS = "electronics"
CLOTHES = "clothes"
@app.get("/products/{category}/{item_id}")
async def get_product_by_category(
category: Category, # 枚举类型,只能传预定义值
item_id: int = Path(ge=1, le=100)
):
return {
"category": category.value,
"item_id": item_id,
"message": f"获取 {category.value} 分类下的商品 {item_id}"
}
此时访问/products/electronics/123会返回正确结果,但访问/products/music/123会报错(music不是枚举值)。
四、动态路由+参数校验的实际应用¶
结合动态路由和参数校验,可以构建更通用、更安全的接口。例如:
from fastapi import FastAPI, Path, Query
from typing import Optional
app = FastAPI()
# 动态生成路由:根据用户ID和订单类型返回订单
@app.get("/users/{user_id}/orders/{order_type}")
async def get_orders(
user_id: int = Path(ge=1, description="用户ID必须是正整数"),
order_type: str = Path(
...,
pattern="^(pending|completed|cancelled)$", # 订单状态只能是这三种
description="订单类型必须是pending/completed/cancelled"
),
page: int = Query(1, ge=1, le=10, description="页码必须是1-10") # 注意:Query用于查询参数,这里仅作示例
):
return {
"user_id": user_id,
"order_type": order_type,
"page": page,
"message": f"获取用户 {user_id} 的 {order_type} 订单,第 {page} 页"
}
- 动态路由:
/users/{user_id}/orders/{order_type}支持任意用户ID和订单类型(只要符合规则)。 - 参数校验:
user_id必须是正整数,order_type必须是预定义状态,page必须是1-10的整数。
五、总结¶
FastAPI的路径参数进阶特性让我们能:
1. 动态路由:通过灵活的路径参数组合,支持多种业务场景(如多维度查询、层级化接口)。
2. 参数校验:自动转换类型、限制范围、枚举值校验,避免非法数据进入接口,提升接口健壮性。
这些特性让接口开发更简单,同时减少了手动校验参数的代码量。如果你正在开发需要灵活路径和严格数据校验的API,FastAPI的路径参数进阶用法会是你的得力助手!
小提示:访问FastAPI自动生成的Swagger文档(/docs),可以直观看到参数校验规则,方便测试和调试。