跳至内容

查询参数和字符串验证

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 会知道它不是必需的。

注意

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

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

额外验证

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

导入 QueryAnnotated

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

  • Query 来自 fastapi
  • Annotated 来自 typing(或 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 的 Python 版本中,你从 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

信息

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,因此参数仍然是可选的。

但是现在,将Query(max_length=50)放在Annotated内部,我们告诉 FastAPI 我们希望它对该值进行**额外的验证**,我们希望它最多有 50 个字符。😎

提示

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

FastAPI 现在将

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

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

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

提示

对于新代码,并在可能的情况下,请使用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版本明确地将其声明为查询参数。

信息

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

= 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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

提示

如果可能,建议使用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查询参数声明为min_length3,并具有默认值"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

提示

如果可能,建议使用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

注意

具有任何类型的默认值(包括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

提示

如果可能,建议使用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

提示

请注意,即使在本例中,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

提示

如果可能,建议使用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

信息

如果您以前没有见过...:它是一个特殊的单值,它是 Python 的一部分,称为“省略号”

它由 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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

提示

Pydantic(它是 FastAPI 中所有数据验证和序列化的基础)在您使用OptionalUnion[Something, None]且没有默认值时具有特殊行为,您可以在 Pydantic 文档中有关 必需字段 的部分了解有关它的更多信息。

提示

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

查询参数列表/多个值

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

https://127.0.0.1:8000/items/?q=foo&q=bar

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

因此,对该 URL 的响应将是

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

提示

要声明一个类型为list的查询参数,就像上面的示例一样,您需要显式地使用Query,否则它将被解释为请求主体。

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

查询参数列表/具有默认值的多个值

您还可以定义一个默认值列表,如果未提供任何值

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

如果您访问

https://127.0.0.1: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

提示

如果可能,建议使用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

注意

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

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

声明更多元数据

您可以添加有关参数的更多信息。

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

注意

请记住,不同的工具可能具有不同的 OpenAPI 支持级别。

其中一些可能无法显示所有声明的额外信息,尽管在大多数情况下,缺少的功能已计划开发。

您可以添加一个title

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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...

那么你可以声明一个 别名,这个别名将被用来查找参数值。

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

弃用参数

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

你必须把它保留一段时间,因为有些客户端还在使用它,但你希望文档能清楚地把它标记为 已弃用

然后将参数 deprecated=True 传递给 Query

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

提示

如果可能,建议使用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

提示

如果可能,建议使用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

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"}

提示

如果可能,建议使用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"}

提示

如果可能,建议使用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"}

回顾

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

通用验证和元数据

  • 别名
  • 标题
  • 描述
  • 已弃用

针对字符串的特定验证

  • 最小长度
  • 最大长度
  • 模式

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

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