跳转至

查询参数和字符串校验

FastAPI 允许为参数声明附加信息与校验。

示例如下:

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

查询参数 q 的类型是 Optional[str],即它的类型是 str,但也可以是 None(其实,是它的默认值为 None), q 是可选参数。

笔记

默认值是 None,FastAPI 因此把 q 当作可选参数。

FastAPI 不使用 Optional[str] 中的 Optional,但 Optional 可以让编辑器提供更好的支持,有助于检查错误。

附加校验

接下来,添加一些约束条件:即使 q 是可选的,但只要提供了该参数,该参数的长度就不能超过 50 个字符

导入 Query

首先,从 fastapi 导入 Query

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

Query 为默认值

接下来,把 Query 作为查询参数的默认值,并把 max_length 参数设置为 50:

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

此处必须用 Query() 替换函数里的默认值 None。在此,可以把默认值设置为 Query(default=None),它与定义的默认值效果相同。

所以:

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

……让参数变为可选,可使用:

q: Union[str, None] = None

Python 3.10 以上版本时:

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

……让参数变为可选,可使用:

q: str | None = None

Query 能显式声明查询参数。

说明

注意,FastAPI 关注以下内容:

= None

或:

= Query(default=None)

并且通过 None 识别出查询参数是可选的。

Union[str, None] 只是为了让编辑器提供更好的支持,并不会告诉 FastAPI 该参数是可选的。

这样就可以为 Query 传递更多参数。本例使用 max_length 参数约束字符串:

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

这行代码会校验数据,在数据无效时显示错误信息,并在 OpenAPI 概图的路径操作中存档该参数。

添加更多校验

FastAPI 还支持 min_length 参数:

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

添加正则表达式

FastAPI 还能定义必须与正则表达式匹配的参数:

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, regex="^fixedquery$"
    )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
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, regex="^fixedquery$")
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

这个指定的正则表达式通过以下规则检查接收到的参数值:

  • ^:以该符号之后的字符开头,符号之前没有字符
  • fixedquery:参数值应与 fixedquery 完全匹配
  • $:到此符号结束,fixedquery 后没有其他字符

就算搞不定正则表达式也不用担心,很多人都觉得正则表达式很难。其实不用正则表达式也可以完成很多工作。

但只要您需要使用正则表达式,请记住,FastAPI 对此提供支持。

默认值

Query 的第一个参数是查询参数的默认值。不仅可以使用 None,还可以使用其他值。

如,声明 min_length3,默认值是 fixedquery 的查询参数 q

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

笔记

声明了默认值让该参数成为可选参数。

标记为必选

不需要声明校验或元数据时,只要不声明默认值,就可以把查询参数 q 变为必选参数,例如用:

q: str

或使用以下代码:

q: Union[str, None] = None

但现在是用 Query 声明该参数,示例如下:

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

因此,使用 Query 把查询参数声明为必选时,第一个参数的值应为 ...

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

使用省略号(...)的必选项

还有一种可以显示声明必选值的备选方式。即使用字面值 ... 设置 default 参数:

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 是一种有效类型,但仍要使用 default=...

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

提示

Pydantic 负责实现 FastAPI 中的数据验证与序列化,它支持一种特殊行为,即使用 OptionalUnion[Something, None] 而无须默认值, 详见 Pydantic 文档中关于 必选可选字段的说明。

用 Pydantic 的 Required 替换省略号(...

如果不想使用 ...,可以从 Pydantic 导入 Required

from fastapi import FastAPI, Query
from pydantic import Required

app = FastAPI()


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

提示

记住,大部分情况下,仅需省略 default 参数即可设置必选,因此通常无须使用 ...Required.

查询参数列表 / 多个值

Query 显式定义查询参数时,还可以让它接收一组值,换句话说,就是接收多个值。

例如,在 URL 中声明多个查询参数 q 的代码如下:

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

然后,输入以下网址:

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

就可以在路径操作函数查询参数 q 中以 Python 列表的形式接收查询参数 q 的多个值(foobar)。

因此,该 URL 的响应为:

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

提示

如上例所示,把查询参数的类型声明为 list,要显式使用 Query,否则该参数会被解释为请求体。

API 文档会进行响应更新,允许使用多个值:

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

FastAPI 还支持在未给定值时,为 list 定义默认值:

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

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,就没有这种校验。

声明更多元数据

FastAPI 还支持为参数添加更多信息。

这些信息包含在 OpenAPI 概图里,用于 API 文档和外部工具。

笔记

注意,不同工具对 OpenAPI 的支持可能不同。

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

Query 添加 title 参数:

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

description 参数:

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

参数别名

假设要使用的查询参数是 item-query

如下所示:

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

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

最接近的有效名称是 item_query

但如果必须在 URL 中使用 item-query ……

需要使用 alias 参数声明别名,用于在 URL 中查找这个参数值:

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

弃用参数

如果不再需要某个参数。

但因为某些客户端还在使用该参数,而不得不保留一段时间,此时,需要在文档中把它显示为已弃用

为此,要在 Query 中使用参数 deprecated=True

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,
        regex="^fixedquery$",
        deprecated=True,
    )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
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,
        regex="^fixedquery$",
        deprecated=True,
    )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

API 文档显示如下:

从 OpenAPI 中排除

从生成的 OpenAPI 概图中排除查询参数(从自动文档系统),把 Queryinclude_in_schema 参数设置为 False

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

小结

FastAPI 支持为查询参数声明更多校验和元数据。

常用于校验和元数据的参数包括:

  • alias
  • title
  • description
  • deprecated

专用于校验字符串的参数包括:

  • min_length
  • max_length
  • regex

本章介绍了如何校验字符串

下一章介绍如何校验数值等其他类型。