Skip to content

28|路径参数与查询参数

HTTP 请求把数据传给后端有三种常见方式:URL 路径、查询字符串、请求体。路径参数用于标识资源(如文章 ID),查询参数用于过滤和分页(如 ?page=2)。这篇把这两种参数在 FastAPI 中的声明和校验讲清楚。

一、路径参数

路径参数写在 URL 路径中,用 {} 标记:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/articles/{article_id}")
async def get_article(article_id: int):
    return {"id": article_id}

FastAPI 根据类型注解自动把路径中的字符串转成对应类型。访问 /articles/123 时,article_id 是整数 123;访问 /articles/abc 时,FastAPI 自动返回 422 错误,提示参数类型不匹配。

多个路径参数

python
@app.get("/users/{user_id}/orders/{order_id}")
async def get_order(user_id: int, order_id: int):
    return {"user": user_id, "order": order_id}

枚举约束

路径参数只能是固定值时,用 Enum 约束:

python
from enum import Enum

class Env(str, Enum):
    dev = "dev"
    test = "test"
    prod = "prod"

@app.get("/config/{env}")
async def get_config(env: Env):
    return {"env": env}

访问 /config/dev 正常返回,访问 /config/invalid 返回 422,文档中也会列出允许的枚举值。

路径参数校验

python
from fastapi import Path

@app.get("/articles/{article_id}")
async def get_article(
    article_id: int = Path(..., ge=1, description="文章ID")
):
    return {"id": article_id}
参数作用
...必填( ellipsis 表示没有默认值)
ge=1必须大于等于 1
description文档中的描述

其他校验:gt(大于)、le(小于等于)、lt(小于)。

二、查询参数

查询参数写在 URL 的 ? 后面,用于过滤、排序、分页:

python
@app.get("/articles")
async def list_articles(
    skip: int = 0,
    limit: int = 20,
    status: str | None = None,
):
    return {"skip": skip, "limit": limit, "status": status}

请求示例:

URL参数值
/articlesskip=0, limit=20, status=None
/articles?skip=10skip=10, limit=20, status=None
/articles?limit=50&status=publishedskip=0, limit=50, status="published"

有默认值的查询参数是可选的,不传就用默认值。str | None = None 表示可选参数,不传时值为 None

布尔查询参数

python
@app.get("/articles")
async def list_articles(active: bool = True):
    return {"active": active}
URL参数值
/articlesTrue(默认值)
/articles?active=trueTrue
/articles?active=1True
/articles?active=falseFalse
/articles?active=0False

查询参数校验

python
from fastapi import Query

@app.get("/articles")
async def list_articles(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
):
    return {"skip": skip, "limit": limit}

limit 的范围被限制在 1~100,防止前端传 limit=999999 拖垮数据库。

必填查询参数

python
@app.get("/search")
async def search(q: str):   # 没有默认值,必填
    return {"query": q}

不传 q 时返回 422:

json
{"detail": [{"loc": ["query", "q"], "msg": "field required", "type": "value_error.missing"}]}

三、路径参数 vs 查询参数

维度路径参数查询参数
位置URL 路径中? 后面
必填性必填(URL 结构决定)通常可选
用途标识资源过滤、排序、分页、配置
示例/articles/123/articles?page=2

RESTful 设计中,路径参数是 URL 的结构性部分,删掉后 URL 就不完整;查询参数是附加信息,删掉后资源本身仍能访问。

四、常见错误

路径参数和函数参数名不一致

python
# 错误:路径中叫 article_id,函数参数叫 id
@app.get("/articles/{article_id}")
async def get_article(id: int):   # FastAPI 找不到 article_id 对应的参数
    return {"id": id}

# 正确:名字一致
@app.get("/articles/{article_id}")
async def get_article(article_id: int):
    return {"id": article_id}

路径参数没有类型注解

python
# 错误:没有类型注解,FastAPI 不会校验,文档中显示为 string
@app.get("/articles/{id}")
async def get_article(id):
    return {"id": id}

# 正确
@app.get("/articles/{id}")
async def get_article(id: int):
    return {"id": id}

查询参数没有设上限

python
# 危险:limit 无上限,数据库可能返回海量数据
@app.get("/articles")
async def list_articles(limit: int = 20):
    ...

# 正确:限制最大值
@app.get("/articles")
async def list_articles(limit: int = Query(20, ge=1, le=100)):
    ...