FastAPI 路径参数(Path Parameters)完整实战指南:声明语法、类型转换与校验、Enum 限定值及 path 转换器
路径参数是 RESTful API 设计中最基础也最常用的部分,它把可变内容直接放进 URL 路径(例如 /items/42 中的 42)。本指南以 FastAPI 官方 Tutorial 的《Path Parameters》章节为主线(对应本仓库 docs/hi/docs/tutorial/path-params.md,其英文原文位于 docs/en/docs/tutorial/path-params.md),带你掌握:如何用与 Python format string 相同的语法声明路径参数、如何借助标准 Python 类型注解获得数据解析与校验、如何利用 Enum 限定合法取值,以及如何让路径参数本身承载一段完整路径。读完你将能独立写出带类型安全路径参数、可自动生成交互式文档的 FastAPI 接口。
用 format string 语法声明路径参数
FastAPI 允许你在 @app.get(...) 这类装饰器中,用与 Python 字符串格式化相同的花括号语法把 URL 中的某一段声明为"参数"或"变量"。第一个示例来自 docs_src/path_params/tutorial001_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
路径中的 {item_id} 是一个路径参数占位符:当请求打到 /items/foo 时,foo 这一小段会被提取出来,作为名为 item_id 的参数传入你的函数 read_item(item_id),因此返回体就是:
{"item_id":"foo"}
要点是函数参数名必须与 URL 路径中花括号内的名字保持一致——FastAPI 靠名字把 URL 片段绑定到函数参数上。
如何运行验证
把上述代码保存为 main.py(仓库文档源码中每个教程文件都对应独立可运行的应用),再用任意 ASGI 服务器启动即可,例如 uvicorn main:app --reload。随后在浏览器访问 http://127.0.0.1:8000/items/foo,即可看到上面的 JSON 响应。
用标准类型注解声明路径参数类型
路径参数不必永远是字符串。你可以在函数签名上为它标注 Python 标准类型,见 docs_src/path_params/tutorial002_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
这里把 item_id 声明为 int。类型注解会带来三重连锁收益:
- 编辑器支持:函数体内可得到类型推断、错误检查与自动补全;
- 数据解析:请求 URL 中的字符串会被转换为 Python 数据类型;
- 数据校验:无法转换的值会被拒绝并返回结构化错误。
数据转换(Parsing):字符串 → int
当你访问 http://127.0.0.1:8000/items/3 时,返回的是:
{"item_id":3}
注意响应里是 3(不带引号)——函数收到并返回的是 Python 的 int,而不是字符串 "3"。也就是说,仅凭 item_id: int 这一处声明,FastAPI 就自动完成了把 HTTP 请求字符串解析成 Python 数据的动作。
数据校验:非法输入返回结构化 422 错误
如果访问 http://127.0.0.1:8000/items/foo,因为 "foo" 无法解析成 int,你会收到一个格式规范的 HTTP 校验错误(状态码 422):
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
同理,如果把 4.2(浮点数)当作 int 传入,如访问 http://127.0.0.1:8000/items/4.2,也会出现完全相同的错误——int 声明不接受浮点字面量。
这个错误对象的结构很有价值:
| 字段 | 含义 |
|---|---|
type |
错误类别,如 int_parsing(整型解析失败)、后续会遇到的 enum(枚举值不在允许范围内)等 |
loc |
出错位置的定位链,这里是 ["path", "item_id"],精确指明错误发生在路径参数 item_id 上 |
msg |
面向开发者的可读错误说明 |
input |
实际传入的原始值,便于排查 |
因为错误精确指出了是哪一处、哪一个参数、什么样的输入校验未通过,你在开发、调试与 API 对接的代码时会非常省力。这条校验逻辑并非 FastAPI 单独实现,而是由 Pydantic 完成的(详见后文"底层校验引擎"一节),其 v2 错误格式即为此结构。仓库中大量测试(例如 tests/test_tutorial/test_path_params/test_tutorial005.py)断言了 422 响应与错误 JSON 结构,可作为验证依据。
一份类型声明,自动获得 Swagger UI 交互文档
打开 http://127.0.0.1:8000/docs,FastAPI 会基于你写下的路径与类型声明,自动生成交互式 API 文档(集成 Swagger UI):
注意上图中 item_id 被正确渲染为 integer 类型、required: true——这些信息完全来自函数签名里的 item_id: int,你无需额外写任何配置。
基于 OpenAPI 标准的替代文档:ReDoc
FastAPI 生成的接口 schema 遵循 OpenAPI 标准,因此天然兼容大量生态工具。官方自带的替代文档(基于 ReDoc)位于 http://127.0.0.1:8000/redoc:
同样由于 schema 遵循 OpenAPI 标准,社区存在大量可兼容工具,包括面向多种语言的客户端代码生成工具。FastAPI 自身还暴露 /openapi.json,导出的正是这份标准化 schema。
底层校验引擎:Pydantic
所有数据校验在内核层面都由 Pydantic 执行,因此你直接享受 Pydantic 的全部能力与可靠性。类型声明的玩法不止 int,你还可以用 str、float、bool 以及许多更复杂的数据类型,其中相当一部分会在教程后续章节(如 Query 参数与请求体章节)继续展开。仓库中 fastapi/_compat/ 与 fastapi/dependencies/utils.py 等模块承载了参数字段的构建与解析管线,校验细节交由 Pydantic 完成。
路径匹配顺序:固定路径必须先于参数路径声明
当路径操作(path operation)越来越多,你会遇到"固定路径 vs 参数路径"重叠的场景。例如想用 /users/me 获取当前用户信息,又用 /users/{user_id} 按 ID 获取某个用户的数据:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}
关键原则:/users/me 必须在 /users/{user_id} 之前声明(见 docs_src/path_params/tutorial003_py310.py)。因为路径操作按声明顺序逐一匹配,若反了顺序,/users/me 会先被 /users/{user_id} 匹配到——FastAPI 会"以为"收到的是值为 "me" 的参数 user_id,从而返回错误的结果。
同理,你不能重复定义同一个路径操作。即使注册了两个路径完全相同的接口(见 docs_src/path_params/tutorial003b_py310.py):
from fastapi import FastAPI
app = FastAPI()
@app.get("/users")
async def read_users():
return ["Rick", "Morty"]
@app.get("/users")
async def read_users2():
return ["Bean", "Elfo"]
由于匹配总是取先命中的路由,永远只会执行第一个 /users(返回 ["Rick", "Morty"]),第二个定义实际上不可达。这与 FastAPI 路由表的顺序匹配机制一致——在 fastapi/routing.py 中,每条路由都会把路径编译为正则表达式并按注册顺序参与匹配。对应测试见 tests/test_tutorial/test_path_params/test_tutorial003.py 与 tests/test_tutorial/test_path_params/test_tutorial003b.py。
用 Python Enum 限定路径参数的预定义取值
当路径参数的合法取值应当被限定为一组固定的值(例如枚举机器学习模型名)时,标准 Python 的 Enum 就是最自然的工具。
创建 str 与 Enum 的子类
首先导入 Enum 并创建一个同时继承 str 与 Enum 的子类:
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
这里的继承有两个讲究:
- 继承
str后,API 文档能识别这些值属于string类型并正确渲染; - 每个类属性(
alexnet = "alexnet"等)的取值就是该参数可用的合法值。
顺带一提:
AlexNet、ResNet、LeNet只是机器学习(深度学习)模型架构的名字,用于示例而已。
声明一个枚举类型的路径参数
用上面创建的 ModelName 作为类型注解来声明路径参数:
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
完整代码见 docs_src/path_params/tutorial005_py310.py。因为合法取值已预定义,交互式文档会把可选项清晰地呈现出来:
与枚举成员协同工作
传入路径参数后,函数收到的 model_name 是一个**枚举成员(enumeration member)**而非普通字符串,你可以:
- 与枚举成员比较:用
model_name is ModelName.alexnet(is或==均可)判断命中的是哪个分支; - 获取枚举的原始值:用
model_name.value拿到真正的字符串值,例如访问/models/lenet时model_name.value == "lenet"。也随时可用ModelName.lenet.value直接取得常量"lenet"; - 返回枚举成员:可以直接把枚举成员放进返回值(甚至嵌套在
dict里),FastAPI 会在返回客户端前把它们自动转换(序列化)成对应取值。因此访问/models/alexnet时客户端收到:
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
枚举越界的表现
如果访问 /models/foo(不在合法集合内),FastAPI 同样返回 422,错误对象为:
{
"detail": [
{
"type": "enum",
"loc": ["path", "model_name"],
"msg": "Input should be 'alexnet', 'resnet' or 'lenet'",
"input": "foo",
"ctx": {"expected": "'alexnet', 'resnet' or 'lenet'"}
}
]
}
以上行为由仓库测试 tests/test_tutorial/test_path_params/test_tutorial005.py 完整覆盖:/models/alexnet、/models/lenet、/models/resnet 均返回 200 及各自分支文案,而 /models/foo 返回 422。同时该测试还对 /openapi.json 做了快照断言——OpenAPI schema 中 ModelName 被建模为 {"type": "string", "enum": ["alexnet", "resnet", "lenet"]},这也解释了为何 Swagger UI 能以下拉候选的方式渲染这些取值。
让路径参数包含路径本身:path 转换器
某些场景下,路径参数的取值本身又包含斜杠分隔的路径。例如定义 /files/{file_path},却希望 file_path 能接住 home/johndoe/myfile.txt,即完整 URL 形如 /files/home/johndoe/myfile.txt。
OpenAPI 的限制
OpenAPI 规范并不支持声明"参数内部再含一段路径"——因为这会引入难以测试与定义的匹配场景。即便如此,FastAPI 仍然可以借助 Starlette 提供的内部工具实现该能力,而且 /docs 交互文档依旧正常工作(只是不会额外注明该参数需包含路径)。
使用 path 转换器
语法是在参数名后追加 :path:
/files/{file_path:path}
其中 file_path 是参数名,末尾的 :path 告诉路由该参数应贪婪匹配任意包含 / 的路径。示例见 docs_src/path_params/tutorial004_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
访问 /files/home/johndoe/myfile.txt 时,file_path 收到的值是 home/johndoe/myfile.txt。如果参数需要保留开头的斜杠(例如绝对路径 /home/johndoe/myfile.txt),URL 中会出现双斜杠:/files//home/johndoe/myfile.txt——files 与 home 之间是两个连续的 /。这两条路径的行为在仓库测试 tests/test_tutorial/test_path_params/test_tutorial004.py 中被明确断言:前者得到 "file_path": "home/johndoe/myfile.txt",后者得到 "file_path": "/home/johndoe/myfile.txt"。
从实现上看,这种带转换器(convertor)的路由会连同参数编译逻辑一起被处理:在 fastapi/routing.py 中,路由通过 compile_path(path) 一次性得到 path_regex、path_format 与 param_convertors 三件套,{file_path:path} 里的 :path 正是被这类转换器识别并赋予"可含斜杠"语义;匹配请求时再经 fastapi/routing.py 的 param_convertors[key].convert(value) 把 URL 片段转为参数值。
小结
在 FastAPI 中,仅凭短小、直观的标准 Python 类型声明,一次声明就能同时得到:
- 编辑器支持:类型错误检查、自动补全等;
- 数据解析:把 HTTP 请求中的字符串转换为 Python 数据;
- 数据校验:不合法的输入得到带精确定位的结构化 422 错误;
- 接口标注与自动文档:Swagger UI、ReDoc 与
/openapi.json自动生成,兼容 OpenAPI 生态。
声明路径参数时只需记住三条经验法则:
- 函数参数名与 URL 花括号中的名字必须一致,类型用标准 Python 注解即可;
- 固定路径(如
/users/me)务必声明在带参数的路径(如/users/{user_id})之前,且不要重复定义相同路径; - 想让参数承接任意嵌套路径,用
{file_path:path}语法,并留意双斜杠带来的前导斜杠处理。
本指南对应的全部可运行示例位于 docs_src/path_params/,官方测试位于 tests/test_tutorial/test_path_params/,你可以直接在仓库中对照运行、验证,作为学习路径参数的首选参考资料。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00


