首页
/ FastAPI 路径参数(Path Parameters)实战指南:声明语法、类型校验、枚举约束与路径转换器

FastAPI 路径参数(Path Parameters)实战指南:声明语法、类型校验、枚举约束与路径转换器

2026-09-06 18:38:51作者:彭桢灵Jeremy

本文以 FastAPI 官方教程《Path Parameters》为骨架,系统讲解如何在路径操作(path operation)中用 Python 格式字符串语法声明路径参数,并借助类型注解一次性获得数据解析、数据校验、编辑器辅助与自动 API 文档。读完本文,你将掌握 {param}{param:type} 等声明写法,理解 int/str/Enum 在路径中的行为差异、路由声明顺序的坑,以及如何让一个路径参数本身包含多层路径(如 /files/home/johndoe/myfile.txt),并能直接照搬文中的可运行示例与测试结论进行开发与排错。

用 Python 格式字符串语法声明路径参数

FastAPI 允许你用与 Python 格式字符串完全相同的语法,在路径操作的路径中声明"参数"或"变量"——用花括号 {} 包住参数名。以仓库示例 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} 就是路径参数。请求到达时,URL 中该段的具体取值会被当作函数参数 item_id 传入:

  • 路径 /items/foo → 参数 item_id = "foo"
  • 路径 /items/bar → 参数 item_id = "bar"

运行该示例(例如 uvicorn docs_src.path_params.tutorial001_py310:app --reload)后,在浏览器访问 http://127.0.0.1:8000/items/foo,会得到 JSON 响应:

{"item_id":"foo"}

注意此时 item_id 未经类型声明,FastAPI 会把它当作字符串处理并原样返回。

使用类型注解声明路径参数

路径参数的值天然来自 URL 字符串,但你完全可以在函数签名中给它一个标准 Python 类型注解,让 FastAPI 知道期望的类型。示例见 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,表示这个路径参数应当是整数。

提示:类型注解带来的一个直接好处是编辑器支持——在函数体内部,IDE 能基于 int 类型给出错误检查、自动补全等辅助,例如对 item_id 调用字符串方法会被即时标红。

数据转换(Parsing / Serialization):字符串自动变成 int

正是"URL 段都是字符串、但参数声明为 int"这一点,触发了 FastAPI 的自动请求数据转换。访问 http://127.0.0.1:8000/items/3,响应是:

{"item_id":3}

关键点在于:你的函数接收到的(并原样返回的)是 Python int 类型的 3,而不是字符串 "3"。也就是说,FastAPI 已经把你从 HTTP 请求中拿到的字符串自动"解析(parse)"成了声明类型对应的 Python 数据。文档原文特意给出术语定义:这里的 conversion(转换)即通常所说的 serialization/parsing/marshalling——把 HTTP 请求中携带的字符串形态数据,转成 Python 程序中可用的真实类型。

数据校验:类型不匹配时的 422 错误

同样的类型声明还带来自动数据校验。若访问 http://127.0.0.1:8000/items/foo(把非数字 "foo" 传给期望 int 的参数),会得到结构化的 HTTP 校验错误:

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": [
        "path",
        "item_id"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo"
    }
  ]
}

传入 float(例如 http://127.0.0.1:8000/items/4.2)也会触发同样的错误,因为 4.2 无法被解析为 int

这条错误信息对调试极有价值,可逐字段解读:

  • type:错误类别,这里是 int_parsing,表示整数解析失败;
  • loc:定位出错位置,["path", "item_id"] 明确告诉你是路径(而非 query 或 body)中的 item_id 参数出了问题;
  • msg:人类可读的失败原因;
  • input:实际收到的原始输入值,即 "foo"

仓库测试 test_tutorial002.py 对该行为做了逐字断言:请求 /items/item1 必须返回 422,且错误体与上述结构完全一致(测试断言中的 "loc": ["path", "item_id"]"type": "int_parsing" 均可在测试源码中核对)。

自动文档:Swagger UI 中的路径参数呈现

同一份类型声明还驱动了自动生成的交互式 API 文档。在浏览器打开 http://127.0.0.1:8000/docs,即可看到集成了 Swagger UI 的交互文档,界面中会明确标注路径参数 item_id 的类型为 integer、位置为 path、且为必填:

FastAPI 路径参数文档截图:GET /items/{item_id} 显示 item_id 为必填的 integer 路径参数

文档界面还列出了两种响应:200 Successful Response422 Validation Error——后者正对应上一节讲解的校验失败场景,说明错误 Schema 也是自动从声明推导出来的。

基于 OpenAPI 标准的文档与生态工具

FastAPI 生成的 API Schema 遵循 OpenAPI 3.1.0 标准(注:本文仅作背景说明,规范为业界公开标准)。由于底层是标准 Schema,FastAPI 自身还提供了另一套基于 ReDoc 的备选文档,访问 http://127.0.0.1:8000/redoc 即可看到:

FastAPI ReDoc 备选文档截图:同一路径参数以 PATH PARAMETERS 形式展示类型与必填性

同样的标准 Schema 还能被大量第三方兼容工具消费——包括面向多种编程语言的代码生成工具(如各语言的 API Client 生成器)。这正是"只声明一次、处处受益"的标准化红利。

你还可以直接访问 http://127.0.0.1:8000/openapi.json 查看原始 Schema。测试文件 test_tutorial002.py 中的 test_openapi_schema 用例对 Schema 做了完整快照断言,可以看到路径参数被生成为:

{
  "in": "path",
  "name": "item_id",
  "required": true,
  "schema": {
    "title": "Item Id",
    "type": "integer"
  }
}

注意其中 "required": true 是路径参数区别于 query 参数的重要特征——路径参数必须出现在 URL 中,天然必填。

底层由 Pydantic 完成校验

FastAPI 所有的数据校验在底层都由 Pydantic 完成(见 Pydantic 官方文档,此处仅作背景)。也就是说,你在路径参数上获得的校验能力与 Pydantic 模型字段的校验是同一套机制,因此可以放心地把复杂校验交给类型系统。

同样的声明方式不只适用于 int,还能用于 strfloatbool 以及许多更复杂的数据类型——后续章节(如查询参数、请求体、模型嵌套等)会逐一展开。

路由声明顺序很重要

当同时存在固定路径与带参数的路径时,声明顺序会直接影响匹配结果。由于路径操作按注册顺序逐个匹配,你必须把固定路径放在参数化路径之前。示例见 tutorial003_py310.py

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} 之前,否则访问 /users/me 时,先匹配到的 /users/{user_id} 会把 "me" 当作 user_id 参数值处理,导致固定路径永远无法命中。

同理,你不能重定义一个路径操作。看 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"]

两个操作都指向 GET /users,由于路径先匹配到第一个,实际生效的永远是 read_users(返回 ["Rick", "Morty"]),第二个 read_users2 永远不会被调用。这条规则提醒你在设计路由时要保持路径唯一、避免歧义。

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

如果你的接口只希望接受一组固定取值,可以把路径参数声明为标准的 Python Enum(枚举),让 FastAPI 自动完成"白名单校验",并在文档中以下拉框形式呈现。

创建 Enum

示例见 tutorial005_py310.py

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()

要点是:类同时继承 strEnum。继承 str 让自动文档能够识别出取值属于字符串类型并正确渲染;随后用类属性定义可用的固定取值。上面 ModelName 的三个取值 alexnetresnetlenet 是机器学习(深度学习)模型架构的名字,用作演示素材。

声明路径参数并查看文档

定义好枚举后,在路径操作中把参数类型注解为枚举类即可(见上例 get_model):

@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"}

由于可用取值已被预定义,交互文档会非常友好地展示可选值。打开 http://127.0.0.1:8000/docs,路径参数 model_name 会显示为带下拉选择器的 string 类型:

FastAPI 枚举路径参数文档截图:GET /models/{model_name} 提供 alexnet/resnet/lenet 下拉选项

如果客户端传入枚举之外的取值(如 /models/tiny-llm),FastAPI 同样会返回 422 校验错误。

枚举成员的三种用法

声明为枚举后,函数中拿到的参数值是枚举成员(enumeration member)而非裸字符串,可配合如下三种操作:

1. 比较枚举成员:用 is 与枚举类成员直接比较,如 if model_name is ModelName.alexnet:,语义清晰且类型安全。

2. 获取枚举值:用 model_name.value 取回真实值(这里是 str)。通用写法即 your_enum_member.value。例如你也可以直接用 ModelName.lenet.value 访问到字符串 "lenet"。示例中用 if model_name.value == "lenet": 判断 lenet 取值。

3. 返回枚举成员:可以直接把枚举成员作为响应返回,即便嵌在 JSON 对象(如 dict)里也没问题——返回给客户端前,FastAPI 会自动把它们序列化成对应的值。比如访问 /models/alexnet,客户端收到的 JSON 是:

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

注意 model_name 字段在 JSON 里是字符串 "alexnet",而不是对象或内部表示。

让路径参数包含路径:路径转换器 :path

有时你的路径参数本身需要容纳一段"路径"。例如路径 /files/{file_path} 希望 file_path 等于 home/johndoe/myfile.txt,对应 URL 为 /files/home/johndoe/myfile.txt

OpenAPI 标准并不支持声明"参数内含路径"——这种场景难以测试与定义,所以标准层面被排除了。但 FastAPI 仍然可以做到:借助其底层 Web 框架 Starlette 提供的内部工具,且在自动文档依然可用(只是文档不会额外说明该参数需要包含路径)。

写法是在参数名后追加 :path,即使用 path convertor(路径转换器)

/files/{file_path:path}

其中参数名是 file_path,尾部 :path 告诉路由该段应匹配任意路径(可包含多个 /)。完整示例见 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}

提示:如果你希望匹配到的 file_path 自带前导斜杠,例如 /home/johndoe/myfile.txt,那么 URL 中要在 fileshome 之间写双斜杠/files//home/johndoe/myfile.txt/files/ 之后的所有内容(含中间的斜杠)都会被捕获到 file_path 中。

小结:一次类型声明,全套开发能力

通过在路径操作函数里使用简短、直观、符合 Python 习惯的类型注解,FastAPI 为你同时带来了:

  • 编辑器支持:类型错误检查、自动补全等;
  • 数据解析:把来自 HTTP 请求的字符串转换成对应 Python 类型;
  • 数据校验:类型不匹配时返回结构化、可定位的 422 错误;
  • API 注解与自动文档:驱动 Swagger UI、ReDoc 及下游标准工具。

这些能力只需声明一次(type annotation 声明于函数签名),FastAPI 便在运行时统一完成路由匹配、参数提取、校验与 Schema 生成。本文的每个行为——从 int 转换、422 错误结构,到 OpenAPI Schema 快照——都能在仓库测试目录 test_path_params 下找到一一对应的用例文件(如 test_tutorial001.pytest_tutorial002.pytest_tutorial003.pytest_tutorial003b.pytest_tutorial004.pytest_tutorial005.py),配合 docs_src/path_params 下的示例源码,可完整复现并验证文中全部结论。

路径参数只是 FastAPI 参数体系的入门一环。掌握了这里的"声明即得一切"心智模型后,后续教程中的查询参数、请求体、Header/Cookie 参数等章节都遵循同样的设计哲学,可以无缝迁移。

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