首页
/ FastAPI 路径参数(Path Parameters)完整实战指南:声明语法、类型转换与校验、Enum 限定值及 path 转换器

FastAPI 路径参数(Path Parameters)完整实战指南:声明语法、类型转换与校验、Enum 限定值及 path 转换器

2026-09-07 15:12:15作者:田桥桑Industrious

路径参数是 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。类型注解会带来三重连锁收益:

  1. 编辑器支持:函数体内可得到类型推断、错误检查与自动补全;
  2. 数据解析:请求 URL 中的字符串会被转换为 Python 数据类型;
  3. 数据校验:无法转换的值会被拒绝并返回结构化错误。

数据转换(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):

Swagger UI 自动文档中 item_id 路径参数被声明为整数,并标注 required

注意上图中 item_id 被正确渲染为 integer 类型、required: true——这些信息完全来自函数签名里的 item_id: int,你无需额外写任何配置。

基于 OpenAPI 标准的替代文档:ReDoc

FastAPI 生成的接口 schema 遵循 OpenAPI 标准,因此天然兼容大量生态工具。官方自带的替代文档(基于 ReDoc)位于 http://127.0.0.1:8000/redoc

ReDoc 形式的自动 API 文档页面

同样由于 schema 遵循 OpenAPI 标准,社区存在大量可兼容工具,包括面向多种语言的客户端代码生成工具。FastAPI 自身还暴露 /openapi.json,导出的正是这份标准化 schema。

底层校验引擎:Pydantic

所有数据校验在内核层面都由 Pydantic 执行,因此你直接享受 Pydantic 的全部能力与可靠性。类型声明的玩法不止 int,你还可以用 strfloatbool 以及许多更复杂的数据类型,其中相当一部分会在教程后续章节(如 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.pytests/test_tutorial/test_path_params/test_tutorial003b.py

用 Python Enum 限定路径参数的预定义取值

当路径参数的合法取值应当被限定为一组固定的值(例如枚举机器学习模型名)时,标准 Python 的 Enum 就是最自然的工具。

创建 strEnum 的子类

首先导入 Enum 并创建一个同时继承 strEnum 的子类:

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" 等)的取值就是该参数可用的合法值。

顺带一提:AlexNetResNetLeNet 只是机器学习(深度学习)模型架构的名字,用于示例而已。

声明一个枚举类型的路径参数

用上面创建的 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 在文档中被呈现为 alexnet、resnet、lenet 三个预定义枚举值

与枚举成员协同工作

传入路径参数后,函数收到的 model_name 是一个**枚举成员(enumeration member)**而非普通字符串,你可以:

  • 与枚举成员比较:用 model_name is ModelName.alexnetis== 均可)判断命中的是哪个分支;
  • 获取枚举的原始值:用 model_name.value 拿到真正的字符串值,例如访问 /models/lenetmodel_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——fileshome 之间是两个连续的 /。这两条路径的行为在仓库测试 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_regexpath_formatparam_convertors 三件套,{file_path:path} 里的 :path 正是被这类转换器识别并赋予"可含斜杠"语义;匹配请求时再经 fastapi/routing.pyparam_convertors[key].convert(value) 把 URL 片段转为参数值。

小结

在 FastAPI 中,仅凭短小、直观的标准 Python 类型声明,一次声明就能同时得到:

  • 编辑器支持:类型错误检查、自动补全等;
  • 数据解析:把 HTTP 请求中的字符串转换为 Python 数据;
  • 数据校验:不合法的输入得到带精确定位的结构化 422 错误;
  • 接口标注与自动文档:Swagger UI、ReDoc 与 /openapi.json 自动生成,兼容 OpenAPI 生态。

声明路径参数时只需记住三条经验法则:

  1. 函数参数名与 URL 花括号中的名字必须一致,类型用标准 Python 注解即可;
  2. 固定路径(如 /users/me)务必声明在带参数的路径(如 /users/{user_id})之前,且不要重复定义相同路径;
  3. 想让参数承接任意嵌套路径,用 {file_path:path} 语法,并留意双斜杠带来的前导斜杠处理。

本指南对应的全部可运行示例位于 docs_src/path_params/,官方测试位于 tests/test_tutorial/test_path_params/,你可以直接在仓库中对照运行、验证,作为学习路径参数的首选参考资料。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388