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

Sean Yeh
Python Everywhere -from Beginner to Advanced
11 min readSep 6, 2023
Hungarian National Assembly, Budapest, photo by Sean Yeh

歡迎大家回來繼續與我一同探索FastAPI的奧秘。記得上次我們聊到了請求主體(request body),那是我們如何透過網路傳送和接收資訊的基礎。今天,我們要進入更有趣的領域:「路徑和查詢參數」以及「參數元資料」。這些名詞聽起來是不是有點專業、有點難懂?別擔心,我會用最簡單的方式帶你認識它們。

此外,到了下篇我們還會探討「驗證器」這個神秘的工具。當你在使用FastAPI開發應用程式時,這些都是你必須要知道的重要元素。所以,一起跟我深入這些與parameters相關的主題,讓我們的程式之旅更上一層樓。

路徑參數和查詢參數

還記得在GET請求中,我們如何巧妙地利用路徑參數(Path parameters)和查詢參數(Query parameters)來取得所需的資訊嗎?那麼,當我們使用POST請求時,這些參數又該如何運作呢?

讓我們先看一下FastAPI的強大之處。它不僅允許我們在GET請求中使用這些參數,更讓我們在POST請求中也能夠靈活地組合各種資料類型。想像一下,你可以在一個POST請求中,同時使用請求主體(Request Body)的資料型態、路徑參數(Path parameters)的功能變數,以及查詢參數(Query parameters)。是不是覺得很酷?

讓我們看看下面這段程式碼:

@router.post('/new/{id}')
def create_blog(blog: BlogModel, id: int, version: int = 1):
pass

這裡,我們在一個POST請求中結合了三種不同的資料類型,例如,請求主體的資料型態( blog: BlogModel )、路徑參數的功能變數 (id: int )與查詢參數( version: int = 1 )。這樣的組合方式不僅提高了程式的靈活性,也讓資料的傳遞更加精確。

接下來,我們將透過例子探討這些參數的運作原理,以及如何在FastAPI中有效地使用它們。準備好了嗎?讓我們一起開始吧!

首先,讓我們看看一段基本的FastAPI程式碼,這段程式碼定義了一個create_blog的函式,用於建立一篇新的部落格文章。

from typing import Optional
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter(
prefix="/blog",
tags=["blogs"],
)
class BlogModel(BaseModel):
title: str
content: str
number_comments: int
published: Optional[bool]
@router.post('/new')
def create_blog(blog: BlogModel):
return {"data": blog}

現在,我們想要給這篇部落格文章加上一個獨特的ID,並且還想要加上一個版本號。這樣的話,當我們更新部落格文章時,就可以知道這是第幾個版本了。

所以,我們將create_blog函式進行了一些修改:

@router.post('/new/{id}')
def create_blog(blog: BlogModel, id: int, version: int = 1):
return {
"id": id,
"data": blog,
"version": version
}

這樣,當我們透過POST請求建立一篇新的部落格文章時,就可以同時傳送文章的內容、ID和版本號了。

我們可以到FastAPI提供的工具SwaggerUI裡面確認我們的修改是否正確。只要開啟網址http://127.0.0.1:8000/docs,就可以看到的API文件,並且進行測試。

在SwaggerUI中,你會看到在POST項目裏面,我們的create_blog函式現在有了id和version兩個參數。點選Try it out,就可以進行測試了。這樣的測試方式不僅直觀,而且非常方便,大家可以自己嘗試看看。

比如說,我們分別在id與version選項input欄位中,輸入5與7兩個數字後,按下下方的Execute按鈕執行。

當程式執行後,就可以看到執行結果出現了一個JSON格式的資料,裏面包括我們剛才給的id與version資料以及data的資料。

再來,我們可以觀察到它的網址為http://127.0.0.1:8000/blog/new/5?version=7。在此你會發現網址中new的後面跟著的是路徑參數 5(這是id的部分),而問號後面跟著的是查詢參數 version=7

以上例子驗證了FastAPI允許我們將三種資料類型合併在一起。

參數元資料(Parameter metadata)

接下來,我們要來探討什麼是參數元資料(Parameter metadata)。簡單來說,它是一些附加在參數上的額外資訊,可以幫助我們更好地了解和使用API。

可透過 Query、Path 和 Body匯入參數元資料

參數的元資料可以透過查詢(Query)、路徑(Path)和主體(Body)來匯入,讓我們可以更靈活地管理和使用參數。

現在,我們來看看如何實際應用參數元資料。首先,我們需要匯入相應的套件,如下:

from fastapi import APIRouter, Query, Path, Body

參數元資料可以設定預設值

接著,我們可以為參數設定一些預設值。例如,我們可以為comment_id這個參數設定一個預設值Query(None),如下:

comment_id: int = Query(None)

然後,我們可以建立一個新的方法來建立部落格的評論。在這個方法中,我們會定義一個create_comment函數,並為它添加一些參數,包括blogidcomment_id。這裡的comment_id是一個查詢參數,其類型為int整數。

@router.post("/new/{id}/comment")
def create_comment(
blog: BlogModel,
id: int,
comment_id: int = Query(None)
):
return {"blog": blog, "id": id, "comment_id": comment_id}

加入標題和描述

除了基本的參數設定,我們還可以為參數添加一些額外的資訊,如標題(title)和描述(description)。這些資訊可以幫助使用API的人更好地了解參數的用途和功能。它們將在SwaggerUI文件中顯示。

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

上面的程式碼使用到的Query,必須先在程式的最上面匯入它。因此我們要去檔案的最前面檢查看看是否已經匯入Query。如果你只需要用到Query查詢,就只需要匯入Query,如果還有建立路徑參數的需要,就要匯入Path。

from fastapi import Query

回到上面的程式碼,你會發現我們在comment_id的地方使用了Query。並且在裡面置入了title與description的參數。

comment_id: int = Query(
None,
title="Comment ID的標題",
description="Comment ID的一些說明",
),

接著,到SwaggerUI文件中檢視剛剛的函式。我們可以在SwaggerUI裡面看到資訊description。

但這還不是全部,我們還可以為參數添加一些更高級的設定,比如「別名」和「廢棄標記」。別名可以讓我們在請求中使用不同的參數名稱,而廢棄標記則可以幫助我們管理API的版本和功能。

加入別名與廢棄標記

接下來,我們來看看別名。別名是你可以在請求中傳遞的參數之名稱。

有些時候,基於某種因素,我們會希望參數名稱有一種不同的結構。看看下面的例子,在這個例子中,將會傳遞commentId(其中Id的I大寫、d小寫),而不是 comment_id。這時,就可以加入別名,如下面程式碼中的alias="commentId"

@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",
),
):
return {"blog": blog, "id": id, "comment_id": comment_id}

在還沒加入別名的設定之前,網址中的維持原來的comment_id( comment_id=5 )

一旦加入別名之後,網址就不一樣了。原本的comment_id變成了commentId( commentId=5 )。

以上就是別名的用法。

添加廢棄標記(Deprecation)

另外,如果有廢棄的端點(endpoint),我們可以在參數上設置一個deprecated標記。舉例來說,如果我們想要廢棄這個create_comment時,可以加入一個deprecated標記( deprecated=True )。

@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,
),
):
return {"blog": blog, "id": id, "comment_id": comment_id}

添加標記之後,我們會在SwaggerUI看到這個廢棄的標記(紅色deprecated文字)。

透過這樣的方式,我們可以更好地管理和使用API,當你有不同版本的API定期發布,你可能想要棄用一些功能。因此,它可以讓您更方便地進行版本控制和功能更新。

未完待續… 請看下篇

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

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