跳到内容

路径参数和数值验证

与使用 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

提示

如果可能,请优先使用 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

提示

如果可能,请优先使用 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

信息

FastAPI 在 0.95.0 版本中增加了对 Annotated 的支持(并开始推荐它)。

如果你的版本较旧,尝试使用 Annotated 时会报错。

请确保你将 FastAPI 版本升级到至少 0.95.1 后再使用 Annotated

声明元数据

你可以声明与 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

提示

如果可能,请优先使用 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

提示

如果可能,请优先使用 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

注意

路径参数总是必需的,因为它必须是路径的一部分。即使你将其声明为 None 或设置了默认值,它也不会产生任何影响,它仍然始终是必需的。

按需排序参数

提示

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

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

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

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

如果你将带有“默认值”的参数放在没有“默认值”的参数之前,Python 会报错。

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

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

所以,你可以将函数声明为

提示

如果可能,请优先使用 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
🤓 其他版本和变体
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

但请记住,如果你使用 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

提示

如果可能,请优先使用 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,这可能不那么重要或不必要。

这里有一个可能很有用但你不会经常用到的小技巧

如果你想

  • 声明 q 查询参数,但不使用 Query 或任何默认值
  • 使用 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
🤓 其他版本和变体
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

使用 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

提示

如果可能,请优先使用 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"), 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

提示

如果可能,请优先使用 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

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

同样适用于

  • gtgreater than (大于)
  • leless 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

提示

如果可能,请优先使用 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

提示

如果可能,请优先使用 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(以及你尚未见过的其他内容),你可以像 查询参数和字符串验证 那样声明元数据和字符串验证。

你也可以声明数值验证:

  • gtgreater than (大于)
  • gegreater than or equal (大于或等于)
  • ltless than (小于)
  • leless than or equal (小于或等于)

信息

QueryPath 以及你稍后将看到的其他类都是一个共同的 Param 类的子类。

所有这些类都共享你已经看到过的用于额外验证和元数据的相同参数。

技术细节

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

当它们被调用时,会返回同名类的实例。

所以,你导入的是 Query 函数。当你调用它时,它会返回一个同样名为 Query 的类的实例。

提供这些函数(而不是直接使用类)是为了让你的编辑器不会标记关于其类型的错误。

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