Python新手的FastAPI之旅5:參數與驗證(下)

Sean Yeh
Python Everywhere -from Beginner to Advanced
16 min readSep 6, 2023

--

Hungarian National Assembly, Budapest, photo by Sean Yeh

上一篇我們提到了路徑參數、查詢參數與參數元資料。接下來,讓我們來了解一下什麼是驗證器(Validators)。

驗證器(Validators)

簡單來說,驗證器是一種可以幫助我們檢查和驗證參數值的工具。它可以確保我們的API只接受合法和有效的參數值,從而避免了許多潛在的問題和風險。也就是說,驗證器會驗證那些傳給我們API軟體的參數以及那些取用我們API的軟體傳遞出去的資料。這個功能不僅可以讓我們更好地管理API的參數,還可以幫助我們提高API的穩定性和安全性。

現在,我們來看看如何在FastAPI中使用驗證器。

提供預設值(default value)

首先,我們可以為參數提供一些預設值。例如,我們可以為content這個參數提供一個預設值'Hi there',如下:

content:str = Body('Hi there')

這樣,當我們在請求中不提供content參數時,它的值就會自動設為'Hi there'。這是一種非常方便的方式來設定參數的預設值。

以下面的程式碼create_comment函式來說,我們在這裡添加一個字串類型的content參數,並提供一個預設值'Hi there'

@router.post("/new/{id}/comment")
def create_comment(
blog: BlogModel,
id: int,
comment_id: int = Query(
None,
title="Comment ID的標題",
description="Comment ID的一些說明",
alias="commentId",
deprecated=True,

),
**content: str = Body("Hi there")**
):
return {
"blog": blog,
"id": id,
"comment_id": comment_id,
"content": content
}

儲存後,讓我們到SwaggerUI中更新我們的文件。

當我們提供完整的值時,content參數變為非必填的內容。所以,我們把content刪除,不在請求中傳遞它,並且再執行一次。

然後我們在這裡得到了一個200的回應,而剛才被我們刪除的content值仍然出現了。它的內容是「Hi there」,是個預設值。

必填參數值(non-optional parameters)

以上是提供預設值的最簡單方法。但是,有時候我們可能需要讓某些參數成為必填值(non-optional parameters)。在這種情況下,我們可以使用Python中的省略號()或Ellipsis來表示一個參數是必填的,如下:

content: str = Body(...)

# 或者是
content: str = Body(Ellipsis)

我們可以將前面的程式碼修改看看,並測試。

@router.post("/new/{id}/comment")
def create_comment(
blog: BlogModel,
id: int,
comment_id: int = Query(
None,
title="Comment ID的標題",
description="Comment ID的一些說明",
alias="commentId",
deprecated=True,

),
content: str = Body(...)
):
return {
"blog": blog,
"id": id,
"comment_id": comment_id,
"content": content
}

儲存後,重新整理SwaggerUI的文件,就可以測試看看結果。

像這樣,如果我們在請求中不提供content參數的值,系統就會返回一個錯誤訊息,提示我們必須提供這個參數的值。

所以如果我確實傳content的值,然後執行它,那麼我會看到它正常在運行。

所以,基本上這就是我們讓一個參數變為必須的方法。我們使用省略符號(…)來表示該參數必須提供。

參數的限制

除了必填參數之外,我們還可以為參數設定一些其他的限制。

設定最小長度限制

在FastAPI中,我們可以很容易地為參數設定一些基本的限制,例如最小長度。這可以透過min_length參數來實現,如下:

content: str = Body(..., min_length=12)

我們在三個點的後面,加上最小長度限制(min_length=12)。如此,系統就會自動檢查content參數的值是否滿足最小長度的要求,如果不滿足,就會返回一個錯誤訊息。

設定最大長度和正則表達式限制

除了最小長度之外,我們還可以為參數設定最大長度(max_length)和正則表達式(regex)的限制,如下:

content: str = Body(..., min_length=12, max_length=24, regex="^[a-z\\s]*$"),

這樣,我們就能夠更好地控制參數的值,並確保它們能滿足我們的需求和標準。

結合實際範例

現在,來看看如何在實際的程式碼中使用這些驗證器。首先,我們需要在我們的路由函數中定義參數和驗證器,如下:

@router.post("/new/{id}/comment")
def create_comment(
blog: BlogModel,
id: int,
comment_id: int = Query(
None,
title="Comment ID的標題",
description="Comment ID的一些說明",
alias="commentId",
deprecated=True,

),
content: str = Body(...,
min_length=12,
max_length=24,
regex="^[a-z\\s]*$"))
):
return {
"blog": blog,
"id": id,
"comment_id": comment_id,
"content": content}

在這段程式碼中,我們定義了一個create_comment函數,並為它添加了一些參數和驗證器。這樣,當我們在SwaggerUI中測試這個API時,就可以看到這些驗證器是如何工作的。

總而言之,驗證器是一個非常有用的工具,可以幫助我們更好地管理和控制API的參數。它不僅可以保證我們的API更加穩定和安全,還可以提高我們的開發效率和質量。

字串驗證、多值驗證(Multiple values)

FastAPI支援查詢參數(query parameters)可以有多個值。具有多個參數的查詢看起來會像下面這樣。

localhost:8080/blog/2/comment?commemtId=3&v=1.0&v=1.5&v=2.0

假設我們有一個參數v,多個查詢參數值就會像是 v=1.0&v=1.5&v=2.0 。這些多個值將作為list串列傳遞給我們的函式。

該如何做?才能正確接收到多個查詢參數值?

v: Optional[List[str]] = Query(None)

如上面的程式碼,為了可以正確接收到多個查詢參數值,我們定義一個可選(Optional)的查詢參數,並將其定義為我們期望的類型:「list串列」。

接著,我們可以在程式碼中實際操作看看。

@router.post("/new/{id}/comment/{comment_id}")
def create_comment(
blog: BlogModel, id: int,
comment_title: int = Query(None,
title="Comment Title的標題",
description="Comment Title的一些說明", alias="commentTitle",
deprecated=True,
),
content: str = Body(..., min_length=10, max_length=50, regex='^[a-z\\s]*$'),
version: Optional[List[str]] = Query(None),
):
return {
"blog": blog,
"id": id,
"comment_title": comment_title,
"content": content,
"version": version,
}

因為在程式碼中使用了List,我們需要另外從typing匯入List。

from typing import List

從SwaggerUI檢視,我們可以看到在version的地方會出現一個「Add string Item」的按鈕。按下這個按鈕,就可以不斷的添加數值到串列裡面。

此外,我們也可以提供查詢參數一組串列作為預設值,將上面關於version: Optional…的那一行程式碼改成下面這個樣子:

version: Optional[List[str]] = Query(['1.0', '1,5', '2.0']),

儲存後再回到SwaggerUI中檢視,應該會看到在version的地方,除了出現「Add string Item」的按鈕外,還會給我們三個預設數值(1.0 1.5 2.0)。按下這個按鈕,就可以不斷的添加數值到串列裡面。

數字驗證(Number validators)

除了字串的驗證外,我們也進行數字的驗證。

所以,如果我們想要將參數作為整數傳遞,那麼我們可以在函式的參數中提供一個簡單的驗證。

基本上來說,我們有四種驗證器。例如:

大於

comment_id: int = Path(None, gt=5)

大於等於

comment_id: int = Path(None, ge=5)

當然,有正向就有負向,另外兩個與前兩個恰好相反,分別是小於某個值和小於或等於某個值。

小於

comment_id: int = Path(None, lt=5)

小於等於

comment_id: int = Path(None, le=5)

這些基本上是我們用來驗證數字的所有選項。

接著,讓我們在程式碼中試試看。

@router.post("/new/{id}/comment/{comment_id}")
def create_comment(
blog: BlogModel, id: int,
comment_title: int = Query(None,
title="Comment Title的標題",
description="Comment Title的一些說明", alias="commentTitle",
deprecated=True,
),
content: str = Body(..., min_length=10, max_length=50, regex='^[a-z\\s]*$'),
version: Optional[List[str]] = Query(['1.0', '1,5', '2.0']),

comment_id: int = Path(..., le=5)
):

return {
"blog": blog,
"id": id,
"comment_title": comment_title,
"content": content,
"version": version,
"comment_id": comment_id
}

加上 comment_id: int = Path(..., le=5)後,並且儲存。再回到SwaggerUI中檢視,可以發現多出一個comment_id 欄位。我們可以在此測試看看,是不是無法輸入大於5的數值。

複雜子類型(Complex subtypes)

當我們建立Pedantic模型時,並不僅限於只有基本簡單的類型,如字串或整數等。例如,在我們的BlogModel模型中,除了有字串類型和一個整數類型外,還有一個選擇性(Optional)的發佈日期。這是我們要討論複雜子類型(Complex subtypes)的一種。

class BlogModel(BaseModel):
title: str
content: str
nb_comments: int
published: Optional[bool]

由於Pedantic模型不限於簡單的類型,我們可以在模型中使用list串列。

tags: List[str] = []

例如,我們將BlogModel模型改為如下:

class BlogModel(BaseModel):
title: str
content: str
nb_comments: int
published: Optional[bool]
tags: List[str] = []

並且執行看看:

在SwaggerUI中,可以看到tag的空白串列。

又如,我們可以在模型中使用字典。添加一個包含類型為字串的鍵和類型為字串的值的字典。

metadata: Dict[str, str] = {"key1": "value1"}

也可以使用集合(set)與元組(tuple)等等。

要注意的是,在使用Dict之前,必須在前面從typing套件中匯入:

from typing import Dict

自訂子類型(Custom model subtypes)

除了上面的那些子類型外,我們還可以自己定義子類型。

例如,在我們已經定義的BlogModel模型中,需要一個圖像。我們可以透過下面的定義方式,讓圖像可以被重複使用到API的不同部位。

class Image(BaseModel):
url: str
alias: str

好的,讓我們實際試試看。

class Image(BaseModel):
url: str
alias: str
class BlogModel(BaseModel):
title: str
content: str
nb_comments: int
published: Optional[bool]
tags: List[str] = []
metadata: Dict[str, str] = {"key1": "value1"}
image: Optional[Image] = None

如上面的程式碼,我們在這裡定義一個圖像類別。在這裡面,我們需要一個類型為str字串的URL,以及一個類型為字串的別名。接著,在我們的BlogModel模型中使用這個圖像(image: Optional[Image] = None)。至此,我們在將自定義的子類型用在我們的自訂的BlogModel模型裡面。

存檔後,在SwaggerUI中,可以看到Image已經出現了。

您可以根據需求,添加資料到這個圖像。

所以,實際上我們並不受限於一個單一層級的模型, 您可以讓圖像有其他的子類型,需要多少層級都可以。

所以,以上就是添加不同的子類型的說明,無論是資料的串列,集合,元組或者是自訂的子類型都可以。

結語

在這次探索中,我們一起走過了FastAPI的「路徑和查詢參數」、「參數元資料」以及「驗證器」這些重要節點。希望現在您對這些專業名詞不再感到陌生或困惑,而是有了更深入的認識和理解。

在這些看似高深,但實際上十分實用的主題中,我們學會了如何透過不同的方式來優化和豐富我們的API參數,從而使我們的應用程式更加強大和靈活。無論是設定參數的預設值,還是透過驗證器來設定參數的限制和規則,這每一步都是為了讓我們的API更加健壯和友善。

對於初學者來說,剛接觸這些知識可能一開始會感到有些吃力,但只要持續練習和實作,相信不久後,就能夠運用自如,開發出更加專業和高效的應用程式。

當然,學無止境。FastAPI的世界還有更多精彩和深奧的知識等著我們去探索和學習。在未來的學習之路上,希望能夠一起保持這份探索和學習的熱情,一起探索更多FastAPI的奇妙世界。

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。