Skip to content

查询参数与字符串验证

FastAPI 允许你为参数声明额外的信息和验证。

以这个应用为例:

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

查询参数 q 的类型是 Union[str, None](在 Python 3.10 中是 str | None),这意味着它的类型是 str,但也可能是 None,实际上,默认值是 None,因此 FastAPI 知道它不是必需的。

Note

FastAPI 知道 q 的值不是必需的,因为它的默认值是 None

UnionUnion[str, None] 中将允许你的编辑器提供更好的支持并检测错误。

额外的验证

我们将强制执行,即使 q 是可选的,但只要它被提供,其长度不超过 50 个字符

导入 QueryAnnotated

为了实现这一点,首先导入:

  • Queryfastapi
  • Annotatedtyping(在 Python 3.9 以下版本中从 typing_extensions 导入)

在 Python 3.9 或更高版本中,Annotated 是标准库的一部分,因此你可以从 typing 导入它。

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

在 Python 3.9 以下版本中,你从 typing_extensions 导入 Annotated

它已经随 FastAPI 一起安装。

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Info

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

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

确保你在使用 Annotated 之前升级 FastAPI 版本到至少 0.95.1。

q 参数的类型中使用 Annotated

还记得我之前告诉过你,Annotated 可以用来为你的参数添加元数据吗?在Python 类型介绍中?

现在是时候在 FastAPI 中使用它了。🚀

我们有这样的类型注解:

q: str | None = None
q: Union[str, None] = None

我们要做的是将其包装在 Annotated 中,使其变为:

q: Annotated[str | None] = None
q: Annotated[Union[str, None]] = None

这两个版本的意思是一样的,q 是一个可以为 strNone 的参数,默认情况下它是 None

现在让我们跳到有趣的部分。🎉

q 参数的 Annotated 中添加 Query

现在我们有了这个 Annotated,我们可以在其中放置更多信息(在这种情况下是一些额外的验证),在 Annotated 中添加 Query,并将参数 max_length 设置为 50

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

注意默认值仍然是 None,因此该参数仍然是可选的。

但现在,在 Annotated 中有 Query(max_length=50),我们告诉 FastAPI 我们希望它对这个值有**额外的验证**,我们希望它的最大长度为 50 个字符。😎

Tip

这里我们使用 Query() 是因为这是一个**查询参数**。稍后我们将看到其他类似 Path()Body()Header()Cookie(),它们也接受与 Query() 相同的参数。

FastAPI 现在将:

  • **验证**数据,确保最大长度为 50 个字符
  • 当数据无效时向客户端显示**清晰的错误**
  • 在 OpenAPI 模式中**记录**参数(因此它将显示在**自动文档 UI**中)

替代方案(旧):Query 作为默认值

FastAPI 的早期版本(在 0.95.0 之前)要求你使用 Query 作为参数的默认值,而不是将其放在 Annotated 中,你很可能会看到使用它的代码,所以我将向你解释它。

Tip

对于新代码和尽可能的情况下,使用上面解释的 Annotated。它有多个优点(如下所述),没有缺点。🍰

这就是你如何使用 Query() 作为函数参数的默认值,将参数 max_length 设置为 50:

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

在这种情况下(不使用 Annotated),我们必须用 Query() 替换函数中的默认值 None,现在我们需要通过参数 Query(default=None) 设置默认值,它与定义该默认值(至少对于 FastAPI 而言)具有相同的目的。

所以:

q: Union[str, None] = Query(default=None)

...使参数成为可选的,默认值为 None,与以下代码相同:

q: Union[str, None] = None

而在 Python 3.10 及以上版本中:

q: str | None = Query(default=None)

...使参数成为可选的,默认值为 None,与以下代码相同:

q: str | None = None

Query 版本明确声明它是一个查询参数。

Info

请记住,使参数成为可选的最重要部分是:

= None

或:

= Query(default=None)

因为它将使用 None 作为默认值,从而使参数**不是必需的**。

Union[str, None] 部分允许您的编辑器提供更好的支持,但它并不是告诉 FastAPI 该参数不是必需的部分。

然后,我们可以向 Query 传递更多参数。在这种情况下,是应用于字符串的 max_length 参数:

q: Union[str, None] = Query(default=None, max_length=50)

这将验证数据,在数据无效时显示清晰的错误,并在 OpenAPI 模式 路径操作 中记录参数。

Query 作为默认值或在 Annotated

请记住,当在 Annotated 中使用 Query 时,不能使用 Querydefault 参数。

而是使用函数参数的实际默认值。否则,它将不一致。

例如,这是不允许的:

q: Annotated[str, Query(default="rick")] = "morty"

...因为不清楚默认值应该是 "rick" 还是 "morty"

所以,你应该使用(最好是):

q: Annotated[str, Query()] = "rick"

...或者在旧代码库中你会找到:

q: str = Query(default="rick")

Annotated 的优势

推荐使用 Annotated 而不是函数参数中的默认值,它有多个原因更好。🤓

函数参数**的**默认值**是**实际的默认值,这在 Python 中通常更直观。😌

你可以在**其他地方**调用相同的函数而无需 FastAPI,它将**按预期工作**。如果有一个**必需的**参数(没有默认值),你的**编辑器**会通过错误通知你,Python 也会在你运行它而没有传递必需参数时报错。

当你不使用 Annotated 而是使用**(旧的)默认值样式**时,如果你在**其他地方**调用该函数而没有 FastAPI,你必须**记住**传递参数以使其正常工作,否则值将与你的预期不同(例如 QueryInfo 或类似的东西而不是 str)。而且你的编辑器不会报错,Python 在运行该函数时也不会报错,只有在内部操作出错时才会报错。

因为 Annotated 可以有多个元数据注解,你现在甚至可以使用相同的函数与其他工具,如 Typer。🚀

添加更多验证

你还可以添加一个 min_length 参数:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(default=None, min_length=3, max_length=50),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

添加正则表达式

你可以定义一个 正则表达式 pattern,参数应匹配该模式:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None, min_length=3, max_length=50, pattern="^fixedquery$"
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None, min_length=3, max_length=50, pattern="^fixedquery$"
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

这个特定的正则表达式模式检查接收到的参数值:

  • ^:以以下字符开头,前面没有字符。
  • fixedquery:具有确切的值 fixedquery
  • $:在此结束,fixedquery 之后没有更多字符。

如果你对所有这些 “正则表达式” 概念感到困惑,别担心。它们对许多人来说是一个难题。你仍然可以在不需要正则表达式的情况下做很多事情。

但无论何时你需要它们并去学习它们,要知道你已经可以直接在 FastAPI 中使用它们了。

Pydantic v1 使用 regex 而不是 pattern

在 Pydantic 版本 2 之前和 FastAPI 0.100.0 之前,参数被称为 regex 而不是 pattern,但现在已被弃用。

你仍然可以看到一些代码在使用它:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

但要知道这是已弃用的,应该更新为使用新的参数 pattern。🤓

默认值

当然,你可以使用 None 以外的其他默认值。

假设你希望声明 q 查询参数的最小长度为 3,并且默认值为 "fixedquery"

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Note

具有任何类型的默认值,包括 None,都会使参数成为可选的(非必需的)。

必需参数

当我们不需要声明更多的验证或元数据时,我们可以通过不声明默认值来使 q 查询参数成为必需的,例如:

q: str

而不是:

q: Union[str, None] = None

但我们现在正在使用 Query 声明它,例如:

q: Annotated[Union[str, None], Query(min_length=3)] = None
q: Union[str, None] = Query(default=None, min_length=3)

因此,当你需要在使用 Query 时声明一个值为必需的,你可以简单地不声明默认值:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

请注意,即使在这种情况下 Query() 被用作函数参数的默认值,我们也没有将 default=None 传递给 Query()

不过,最好还是使用 Annotated 版本。😉

使用省略号 (...) 表示必需

有一种替代方法可以显式声明一个值是必需的。你可以将默认值设置为字面值 ...

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Info

如果你以前没见过 ...:它是一个特殊的单值,是 Python 的一部分,被称为 "Ellipsis"

Pydantic 和 FastAPI 使用它来显式声明一个值是必需的。

这将让 FastAPI 知道这个参数是必需的。

必需且可以为 None

你可以声明一个参数可以接受 None,但仍然是必需的。这将强制客户端发送一个值,即使该值是 None

为此,你可以声明 None 是一个有效类型,但仍然使用 ... 作为默认值:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

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

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tip

Pydantic,这是 FastAPI 中所有数据验证和序列化的核心,当你使用 OptionalUnion[Something, None] 而没有默认值时,会有特殊的行为,你可以在 Pydantic 文档中了解更多关于 必需字段 的信息。

Tip

记住,在大多数情况下,当某些内容是必需的时,你可以简单地省略默认值,因此你通常不需要使用 ...

查询参数列表 / 多值

当你使用 Query 显式定义查询参数时,你还可以声明它接收一个值列表,或者换句话说,接收多个值。

例如,要声明一个可以在 URL 中多次出现的查询参数 q,你可以这样写:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
    query_items = {"q": q}
    return query_items
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[list[str], None], Query()] = None):
    query_items = {"q": q}
    return query_items
from typing import List, Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[List[str], None], Query()] = None):
    query_items = {"q": q}
    return query_items

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list[str] | None = Query(default=None)):
    query_items = {"q": q}
    return query_items

Tip

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

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[list[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items

Tip

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

from typing import List, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items

然后,使用类似以下的 URL:

http://localhost:8000/items/?q=foo&q=bar

你将在 路径操作函数 中的 函数参数 q 内收到多个 q 查询参数 的值(foobar),这些值在 Python 的 list 中。

因此,对该 URL 的响应将是:

{
  "q": [
    "foo",
    "bar"
  ]
}

Tip

要声明一个类型为 list 的查询参数,如上例所示,你需要显式使用 Query,否则它将被解释为请求体。

交互式 API 文档将相应更新,以允许多个值:

查询参数列表 / 多值与默认值

你还可以在没有提供任何值时定义一个默认的 list

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items
from typing import List

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[List[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list[str] = Query(default=["foo", "bar"])):
    query_items = {"q": q}
    return query_items

Tip

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

from typing import List

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: List[str] = Query(default=["foo", "bar"])):
    query_items = {"q": q}
    return query_items

如果你访问:

http://localhost:8000/items/

q 的默认值将是:["foo", "bar"],你的响应将是:

{
  "q": [
    "foo",
    "bar"
  ]
}

仅使用 list

你也可以直接使用 list 而不是 List[str](或在 Python 3.9+ 中使用 list[str]):

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list = Query(default=[])):
    query_items = {"q": q}
    return query_items

Note

请记住,在这种情况下,FastAPI 不会检查列表的内容。

例如,List[int] 会检查(并记录)列表的内容是否为整数。但单独的 list 则不会。

///

声明更多元数据

你可以为参数添加更多信息。

这些信息将被包含在生成的 OpenAPI 中,并由文档用户界面和外部工具使用。

/// 注意

请记住,不同的工具对 OpenAPI 的支持程度可能不同。

尽管在大多数情况下,缺失的功能已经计划开发,但有些工具可能不会显示所有额外声明的信息。

///

你可以添加一个 title

//// 标签 | Python 3.10+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.9+

from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.10+ 非 Annotated

/// 提示

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

///

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(default=None, title="Query string", min_length=3),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+ 非 Annotated

/// 提示

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

///

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(default=None, title="Query string", min_length=3),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

以及一个 description

//// 标签 | Python 3.10+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.9+

from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.10+ 非 Annotated

/// 提示

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

///

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None,
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+ 非 Annotated

/// 提示

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

///

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None,
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

参数别名

假设你希望参数名为 item-query

例如:

http://127.0.0.1:8000/items/?item-query=foobaritems

item-query 不是一个有效的 Python 变量名。

最接近的是 item_query

但你仍然需要它确切地为 item-query...

然后你可以声明一个 alias,这个别名将用于查找参数值:

//// 标签 | Python 3.10+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.9+

from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.10+ 非 Annotated

/// 提示

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

///

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+ 非 Annotated

/// 提示

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

///

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

弃用参数

现在假设你不再喜欢这个参数了。

你必须让它在那里待一段时间,因为有些客户端还在使用它,但你希望文档清楚地显示它已被弃用

然后向 Query 传递参数 deprecated=True

//// 标签 | Python 3.10+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.9+

from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.10+ 非 Annotated

/// 提示

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

///

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        pattern="^fixedquery$",
        deprecated=True,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

//// 标签 | Python 3.8+ 非 Annotated

/// 提示

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

///

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        pattern="^fixedquery$",
        deprecated=True,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

////

文档将显示如下:

从 OpenAPI 中排除参数

要从生成的 OpenAPI 模式(以及自动文档系统)中排除查询参数,请将 Query 的参数 include_in_schema 设置为 False

//// 标签 | Python 3.10+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}
////

from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

Tip

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

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: str | None = Query(default=None, include_in_schema=False),
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

Tip

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

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Union[str, None] = Query(default=None, include_in_schema=False),
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

总结

你可以为参数声明额外的验证和元数据。

通用的验证和元数据:

  • alias
  • title
  • description
  • deprecated

特定于字符串的验证:

  • min_length
  • max_length
  • pattern

在这些示例中,你看到了如何为 str 值声明验证。

请参阅下一章节,了解如何为其他类型(如数字)声明验证。