FastAPI路径参数进阶:动态路由与参数校验

在使用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允许我们显式指定参数类型(如intfloat),让参数自动转换和校验。

二、动态路由进阶:让路径参数更灵活

动态路由指的是路径参数可以根据实际需求“动态变化”,支持多种参数组合或特殊格式。

1. 路径参数类型自动转换

FastAPI会自动尝试将路径参数转换为指定类型(如intfloat)。例如,即使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. 路径参数的正则表达式限制

如果需要严格限制路径参数的格式(如只能是字母、数字组合),可以用Pathpattern参数配合正则表达式:

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"),可以用EnumLiteral实现:

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),可以直观看到参数校验规则,方便测试和调试。

小夜