路径参数校验
本教程共 50 篇 · 第 7 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会用
Path()给路径参数加范围约束,用Enum限制只能是几个固定值,用:path让参数能装下整条子路径,并把约束自由组合。
上一章我们知道,给路径参数写一个类型(比如 int)就能自动校验。但很多时候要求更高:id 必须大于 0、颜色只能是红绿蓝之一、文件路径要带斜杠。这一章就讲这些更细的约束。
7-1 用 Path 声明约束
要给路径参数加额外规则,需要用到 Path,并配合 Annotated 写进类型注解里。先看一个限制「最小值」的例子:
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: Annotated[int, Path(title="要查询的商品 ID", ge=1)]
):
return {"item_id": item_id}
这里 Path(ge=1) 表示 item_id 必须是一个整数,且「大于等于 1」(ge = greater than or equal)。访问 /items/0 会直接报错,因为 0 不满足 ge=1;访问 /items/5 则正常。
Note路径参数天生就是必填的——它写在 URL 里,不能省略。所以
Path不需要也不支持把它设成可选,哪怕你写了默认值也会被忽略。
7-2 四个数值范围约束
数字类型的路径参数,常用这四种约束:
| 写法 | 含义 | 中文 |
|---|---|---|
gt | greater than | 大于 |
ge | greater than or equal | 大于等于 |
lt | less than | 小于 |
le | less than or equal | 小于等于 |
把它们组合一下,限定 id 落在 1 到 1000 之间:
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: Annotated[int, Path(title="商品 ID", gt=0, le=1000)]
):
return {"item_id": item_id}
gt=0 表示必须严格大于 0,le=1000 表示最多到 1000。传 0、-5、1001 都会被拦下。
7-3 约束对浮点数同样有效
这些范围约束对 float 一样好使。浮点数场景下 gt 和 lt 特别有用,因为它能表达「大于 0 但小于 1」这种区间,比如 0.5 合法,而 0.0 不合法:
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}/sizes/{size}")
async def read_item(
item_id: Annotated[int, Path(title="商品 ID", ge=0, le=1000)],
size: Annotated[float, Path(title="尺寸", gt=0, lt=10.5)],
):
return {"item_id": item_id, "size": size}
访问 /items/3/sizes/5.5 会正常返回 {"item_id": 3, "size": 5.5};访问 /items/3/sizes/11 则返回 422,因为 11 不满足 lt=10.5。浮点约束常出现在分页大小、金额等场景。
7-4 用 Enum 限定只能是固定值
如果路径参数只能取几个确定的值,比如模型名字只能是 alexnet、resnet、lenet,就用 Python 的 Enum。注意让它同时继承 str 和 Enum:
from enum import Enum
from fastapi import FastAPI
app = FastAPI()
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
return {"model_name": model_name}
继承 str 的好处是:文档能正确识别这些值是字符串,渲染更友好;而且你直接用 model_name.value 就能拿到字符串本身。访问 /models/alexnet 返回 {"model_name": "alexnet"};访问 /models/foobar 会报错,提示合法值只有三个。
在函数内部,model_name 是一个枚举成员,可以拿它和枚举类比较:
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "选了 AlexNet"}
return {"model_name": model_name}
要拿真正的字符串值,用 model_name.value;要拿枚举成员本身,直接返回 model_name 也行,FastAPI 会自动转成对应的字符串再回给客户端。
7-5 用 :path 匹配含斜杠的路径
有时路径参数自身就包含斜杠,比如文件路径 /home/johndoe/myfile.txt。普通路径参数遇到第一个斜杠就截断了,所以要用 Starlette 的路径转换器 :path:
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
注意写法是在花括号里写成 {file_path:path},冒号后面的 path 告诉 FastAPI 这个参数要匹配「一整条路径」。访问 http://127.0.0.1:8000/files/home/johndoe/myfile.txt,返回:
{"file_path": "home/johndoe/myfile.txt"}
Tip如果你的路径以斜杠开头,比如想匹配
/home/...,那 URL 要写成/files//home/...,files和home之间会出现两个斜杠//。这是正常的,第一个斜杠是路由分隔,第二个才是参数内容的一部分。
7-6 把约束组合在一起
Path 的约束可以一次写多个,也能和 Enum、类型一起用。下面这个例子同时限制了 id 的范围,并给参数加了文档标题:
from typing import Annotated
from enum import Enum
from fastapi import FastAPI, Path
app = FastAPI()
class Level(str, Enum):
low = "low"
high = "high"
@app.get("/tasks/{task_id}/{level}")
async def read_task(
task_id: Annotated[int, Path(title="任务 ID", ge=1, le=999)],
level: Level,
):
return {"task_id": task_id, "level": level}
这里 task_id 必须是整数且 1 到 999,level 只能是 low 或 high。多个约束写在一个 Path(...) 里就行,互不冲突。
约束不通过时,FastAPI 会返回状态码 422,响应体里带上出错字段的位置和原因,你不需要自己写任何判断语句。这一点很关键:校验发生在你的函数被调用之前,所以函数体里拿到的值一定已经满足全部约束,可以放心直接使用,不必再补一层防御性检查。
Note
Query、Path以及后面会学到的Body、Header、Cookie都继承自同一个Param类,所以它们支持的约束参数几乎一样。学会一个,其余触类旁通。
7-7 参数顺序不必纠结
使用 Annotated 之后,函数里参数的前后顺序已经无所谓了。FastAPI 按「名字、类型、默认值声明」来认参数,不关心你把它写在第几位。早期没有 Annotated 时,如果你既用了 Path 又想放一个没有默认值的普通参数,顺序写错 Python 会报错,那时要用 * 把后面的参数都变成关键字参数来化解。
现在我们用 Annotated 写法,这个麻烦基本碰不到,知道有这回事即可。建议新项目统一用 Annotated 风格,代码更清晰,也更不容易踩顺序的坑。
7-8 小结
这一章给路径参数上了「紧箍咒」:Path(ge=1, le=1000) 用 gt/ge/lt/le 限制数值范围,对整数和浮点数都有效;Enum 让参数只能取你定义的几个固定值,文档里还会直接列出可选项;{file_path:path} 让参数能吞下整条带斜杠的子路径;这些约束还能任意组合。
下一章我们转向「查询参数」,看看 URL 里 ? 后面那些键值对怎么玩。