Skip to content

路徑參數與數值驗證

🌐 AI 與人類共同完成的翻譯

此翻譯由人類指導的 AI 完成。🤝

可能會有對原意的誤解,或讀起來不自然等問題。🤖

你可以透過協助我們更好地引導 AI LLM來改進此翻譯。

英文版

就像使用 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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

Info

FastAPI 在 0.95.0 版加入並開始推薦使用 Annotated

如果你使用更舊的版本,嘗試使用 Annotated 會出錯。

請確保在使用 Annotated 前,先升級 FastAPI 版本到至少 0.95.1。

宣告中繼資料

你可以宣告與 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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

Note

路徑參數一定是必填,因為它必須是路徑的一部分。即使你將其宣告為 None 或設定預設值,也不會有任何影響,它仍然是必填。

依需求調整參數順序

Tip

如果你使用 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
🤓 Other versions and variants
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

但請記住,若使用 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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

依需求調整參數順序的技巧

Tip

如果你使用 Annotated,這點大概沒那麼重要或必要。

這裡有個小技巧偶爾很好用,不過你大概不常需要。

如果你想要:

  • 不用 Query、也不給預設值就宣告查詢參數 q
  • 使用 Path 宣告路徑參數 item_id
  • 讓它們的順序不同
  • 不使用 Annotated

…Python 有個小語法可以做到。

在函式的參數列表最前面放一個 *

Python 不會對這個 * 做任何事,但它會知道後續的所有參數都必須以關鍵字引數(key-value pairs)方式呼叫,也就是所謂的 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
🤓 Other versions and variants
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

使用 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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=1,代表 item_id 必須是「大於(greater)或等於(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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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 or equal)
  • lt:小於(less than)
  • le:小於或等於(less than or equal)

Info

你之後會看到的 QueryPath 與其他類別,都是共同父類別 Param 的子類別。

它們共享你已經看到的那些用於額外驗證與中繼資料的參數。

技術細節

當你從 fastapi 匯入 QueryPath 等時,它們其實是函式。

呼叫它們時,會回傳同名類別的實例。

也就是說,你匯入的是名為 Query 的函式,而當你呼叫它時,它會回傳同名的 Query 類別實例。

這些函式之所以存在(而不是直接使用類別),是為了避免編輯器標記它們的型別錯誤。

如此一來,你就能使用一般的編輯器與開發工具,而不需要額外設定來忽略那些錯誤。