跳到内容

路径参数和数值校验

与使用 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 等)来检测参数,它不关心顺序。

所以,你可以这样声明你的函数

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)或等于(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

数值校验:大于和小于等于

同样适用于

  • gt:大于(greater than)
  • le:小于(less than)或等于(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(以及其他你还未见过的工具),你可以像在查询参数和字符串校验中一样声明元数据和字符串校验。

你也可以声明数值校验

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

信息

QueryPath 和你稍后会看到的其他类,都是一个共同的 Param 类的子类。

它们都共享你所见过的用于附加校验和元数据的相同参数。

技术细节

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

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

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

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

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