路径参数 Path Parameters
本教程共 50 篇 · 第 6 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会用
{id}在 URL 里放动态参数,理解「声明类型就等于自动校验」,并能看懂报错和文档。
在前面几章里,我们的接口地址都是写死的,比如 / 或 /items/。真实的接口往往要处理「不同的东西」,例如查看 id 为 5 的商品、查看 id 为 99 的用户。这个会变化的部分,就是路径参数。
6-1 用花括号声明路径参数
路径参数写在路由地址的花括号 {} 里。它的名字要和函数参数的名字一样,FastAPI 就会自动把 URL 里的值传进函数。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
把这段代码保存为 main.py,用下面命令启动(开发模式带热重载):
fastapi dev main.py
打开浏览器访问 http://127.0.0.1:8000/items/foo,你会看到返回:
{"item_id": "foo"}
这里 foo 就是路径参数 item_id 的值。它现在是个字符串,因为函数参数 item_id 没有写类型。
Tip等价启动命令还有
uvicorn main:app --reload。我们默认推荐fastapi dev,它是 FastAPI 官方 CLI,输出更友好。
6-2 声明类型,自动完成转换
FastAPI 最爽的一点:你只要给参数写一个 Python 类型,它就帮你把 URL 里的字符串转成对应类型。下面把 item_id 声明成 int:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
访问 http://127.0.0.1:8000/items/3,返回变成了:
{"item_id": 3}
注意返回的是整数 3,不是字符串 "3"。FastAPI 已经把来自 HTTP 请求的字符串 "3",自动解析成了 Python 的 int。你在函数内部拿到的就是真正的整数,可以直接做加减乘除。
6-3 声明类型,自动完成校验
类型不只是用来转换,还顺带做了校验。还是上面的代码,如果你访问 http://127.0.0.1:8000/items/foo,foo 明显不是整数,FastAPI 会直接返回一个清晰的报错:
{
"detail": [
{
"type": "int_parsing",
"loc": ["path", "item_id"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
这个报错很有用:loc 告诉你出错位置是路径里的 item_id;type 告诉你错误类型;msg 是给人看的解释;input 是用户实际传的值。调试接口时这些信息能省不少时间。
如果你传的是小数,比如 http://127.0.0.1:8000/items/4.2,因为 4.2 也不是整数,同样会报类似的错。FastAPI 默认要求 int 就是整数。
6-4 文档里自动出现
光写一句 item_id: int,FastAPI 就帮你把参数类型写进了 OpenAPI 文档。启动后打开 http://127.0.0.1:8000/docs,你会看到可交互的 Swagger UI,里面明确标着 item_id 是整数类型,还能直接在网页上填值测试。
再到 http://127.0.0.1:8000/redoc 看另一套文档界面(ReDoc),同样是自动生成的。因为底层文档遵循 OpenAPI 标准,所以很多第三方工具也能直接读懂你的接口,甚至能自动生成各语言的客户端代码。
Note所有这些能力——编辑器补全、数据转换、数据校验、自动文档——都来自同一行类型声明
item_id: int。你只写一次,好处全拿走。这是 FastAPI 相比很多框架最核心的优势。
这些校验的底层其实由 Pydantic 完成。FastAPI 把你的类型声明交给 Pydantic,由它负责把字符串解析成真正的 Python 类型并做校验,所以你享受的是经过大量实战检验的成熟能力,不用担心自己写的转换代码有疏漏。
6-5 声明的类型不止 int
除了 int,你还可以用其它标准 Python 类型,FastAPI 都会自动转换和校验:
| 类型 | 说明 | 访问示例 |
|---|---|---|
str | 字符串(不写类型时默认就是它) | /items/foo |
int | 整数 | /items/5 |
float | 浮点数 | /items/5.5 |
bool | 布尔值 | /items/true |
uuid.UUID | UUID 标识 | /items/3fa85f64-5717-4562-b3fc-2c963f66afa6 |
比如把参数声明成 float:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: float):
return {"item_id": item_id}
访问 /items/3 会得到 3.0,访问 /items/4.2 会得到 4.2。更多复杂类型我们在后面章节继续展开。
6-6 多个路径参数一起用
一个地址里可以放多个路径参数,函数里按名字对应即可,顺序无所谓:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(user_id: int, item_id: str):
return {"user_id": user_id, "item_id": item_id}
访问 http://127.0.0.1:8000/users/5/items/pen,返回:
{"user_id": 5, "item_id": "pen"}
FastAPI 靠「名字」认参数,不靠位置,所以你写成 async def read_user_item(item_id: str, user_id: int) 也没问题。
6-7 路由顺序很重要
当你同时有「固定路径」和「带参数的路径」时,定义顺序会决定谁能匹配到。看这个例子:
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: int):
return {"user_id": user_id}
访问 /users/me 时,FastAPI 从上往下匹配,先命中 /users/me,返回当前用户。如果把这两段顺序反过来,/users/me 会被 /users/{user_id} 抢走,FastAPI 会以为 user_id 的值是字符串 "me",然后因为 "me" 不是整数而报错。
Note固定路径一定要写在「带参数的路径」前面,否则动态参数会把固定值吞掉。这是新手最常踩的坑。
同一个路径也不要重复注册两次。若你为 /users/{user_id} 写了两个处理函数,后一个不会覆盖前一个,而是永远轮不到执行,因为匹配在第一个就停下了。这类问题不会报错,只会表现为”改了代码却没生效”,排查时优先检查是否有更靠前的路径把请求截住了。
6-8 小结
这一章你学会了路径参数的核心套路:用 {名字} 声明,用「和函数参数同名」接收;给参数写一个类型,FastAPI 就同时帮你做转换、校验、生成文档;访问 /docs 能在线调试;多个参数靠名字匹配、顺序随意;固定路由要放在动态路由之前。
下一章我们给路径参数加上更细的约束,比如整数必须大于 0、只能取几个固定值、甚至匹配带斜杠的路径。