Skip to content

路径参数与数值验证

与使用 Query 为查询参数声明更多验证和元数据的方式相同,你也可以使用 Path 为路径参数声明相同类型的验证和元数据。

导入 Path

首先,从 fastapi 导入 Path,并导入 Annotated

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[str | None, Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: int = Path(title="The ID of the item to get"),
    q: str | None = Query(default=None, alias="item-query"),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from typing import Union

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: int = Path(title="The ID of the item to get"),
    q: Union[str, None] = Query(default=None, alias="item-query"),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Info

FastAPI 从版本 0.95.0 开始支持 Annotated(并开始推荐使用它)。

如果你使用的是旧版本,尝试使用 Annotated 时会遇到错误。

在使用 Annotated 之前,请确保你已经升级 FastAPI 版本至至少 0.95.1。

声明元数据

你可以为 Path 声明与 Query 相同的参数。

例如,要为路径参数 item_id 声明一个 title 元数据值,你可以这样写:

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[str | None, Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[Union[str, None], Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: int = Path(title="The ID of the item to get"),
    q: str | None = Query(default=None, alias="item-query"),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from typing import Union

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: int = Path(title="The ID of the item to get"),
    q: Union[str, None] = Query(default=None, alias="item-query"),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Note

路径参数始终是必需的,因为它必须成为路径的一部分。即使你使用 None 或设置默认值,也不会影响任何内容,它仍然是始终必需的。

按需排列参数顺序

Tip

如果你使用 Annotated,这可能不是那么重要或必要。

假设你想将查询参数 q 声明为必需的 str

并且你不需要为该参数声明任何其他内容,因此你实际上不需要使用 Query

但你仍然需要为 item_id 路径参数使用 Path。并且由于某些原因,你不希望使用 Annotated

Python 会抱怨如果你将一个带有“默认值”的值放在一个没有“默认值”的值之前。

但你可以重新排列它们,并将没有默认值的值(查询参数 q)放在前面。

这对 FastAPI 来说无关紧要。它会通过参数的名称、类型和默认声明(QueryPath 等)来检测参数,而不关心它们的顺序。

因此,你可以将你的函数声明为:

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

但请记住,如果你使用 Annotated,你不会遇到这个问题,因为你不使用函数参数默认值来表示 Query()Path()

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

按需排列参数顺序,技巧

Tip

如果你使用 Annotated,这可能不是那么重要或必要。

这里有一个**小技巧**,可能会很方便,但你不需要经常使用它。

如果你想:

  • 声明没有 Query 或任何默认值的 q 查询参数
  • 使用 Path 声明路径参数 item_id
  • 以不同的顺序排列它们
  • 不使用 Annotated

...Python 有一个特殊的语法可以实现这一点。

在函数的第一个参数位置传递 *。 Python 不会对那个 * 做任何处理,但它会知道所有后续的参数都应作为关键字参数(键值对)来调用,也称为 kwargs。即使它们没有默认值。

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

使用 Annotated 更好

请记住,如果你使用 Annotated,由于你没有使用函数参数的默认值,你不会遇到这个问题,并且你可能不需要使用 *

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

数字验证:大于或等于

使用 QueryPath(以及你稍后会看到的其他类),你可以声明数字约束。

在这里,通过 ge=1item_id 需要是一个“greater than or equal”大于或等于 1 的整数。

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    *, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

数字验证:大于和小于或等于

同样适用于:

  • gt: greater than 大于
  • le: less than or equal 小于或等于
from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
    q: str,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Path
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
    q: str,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    *,
    item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),
    q: str,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

数字验证:浮点数,大于和小于

数字验证也适用于 float 值。

在这里,能够声明 gt 而不是仅仅 ge 变得很重要。通过它,你可以要求一个值必须大于 0,即使它小于 1

因此,0.5 将是一个有效值。但 0.00 则不是。

同样适用于 lt

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    *,
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
    q: str,
    size: Annotated[float, Query(gt=0, lt=10.5)],
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if size:
        results.update({"size": size})
    return results
from fastapi import FastAPI, Path, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    *,
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
    q: str,
    size: Annotated[float, Query(gt=0, lt=10.5)],
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if size:
        results.update({"size": size})
    return results

Tip

如果可能,建议使用 Annotated 版本。

from fastapi import FastAPI, Path, Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(
    *,
    item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
    q: str,
    size: float = Query(gt=0, lt=10.5),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if size:
        results.update({"size": size})
    return results

总结

使用 QueryPath(以及你尚未见过的其他类),你可以以与 查询参数和字符串验证 相同的方式声明元数据和字符串验证。

你还可以声明数字验证:

  • gt: greater than 大于
  • ge: greater than or equal 大于或等于
  • lt: less than 小于
  • le: less than or equal 小于或等于

Info

QueryPath 以及你稍后会看到的其他类都是通用 Param 类的子类。

它们都共享你已经看到的用于额外验证和元数据的相同参数。

"技术细节"

当你从 fastapi 导入 QueryPath 和其他类时,它们实际上是函数。

当调用这些函数时,它们返回同名类的实例。

因此,你导入 Query,它是一个函数。当你调用它时,它返回一个名为 Query 的类的实例。

这些函数存在(而不是直接使用类)是为了让你的编辑器不会在这些类的类型上标记错误。

这样你就可以使用你的普通编辑器和编码工具,而不需要添加自定义配置来忽略这些错误。