路徑參數#

這是 Django Ninja 系列教學的第 10 篇。
上一篇文章中,我們介紹了 Django Ninja 如何處理 HTTP 請求,並強調了它與 Python type hints 之間的緊密結合。
本篇將探討 Django Ninja 中,路徑參數(path parameters)的應用與細節,這在處理 HTTP 請求時極為常見,尤其是在 RESTful API 中。
本文對範例專案的程式碼改動,都集中在這個 PR。
範例專案#
一、什麼是 Path Parameters?#
Path parameters 是構成 URL 的一部分,它們位於網址路徑(path)中的特定位置,根據不同的值(參數)來決定傳入的內容,用來動態指定資源。
我們不妨先了解一下,path 在整個 URL 中的位置:(圖片取自維基百科)

從圖中可以看出,路徑(path)是 URL 的一部分——而且是必要部分。
不過請注意,path parameters 只是 Django、Django Ninja 這類框架所提供的一種「功能」。對 URL 本身而言,path 就是 path,也就是單純的字串而已。
路徑參數例子#
舉一個簡單的例子,讓我們可以更好地理解 path parameters 的概念。
@router.get(path='/posts/{post_id}/') # {post_id} 就是路徑參數
實際請求時,123 就是透過路徑參數,代表特定文章的 id:
可想而知,如果是 456 或 789,你會得到不同的結果。
這使得 API 具有彈性,可以針對不同的資源進行操作,而不必為每一個資源建立不同的路由——它們的端點與路由都是相同的,只是「參數」不同。
二、範例專案改動#
接下來,讓我們透過範例專案的程式碼,一邊講解一邊演示本文內容。
但我們要先進行兩個改動。
改動一:取消一級路由前綴#
取消一級路由前綴 /posts/ 和 /user/,讓 view 函式的 router 裝飾器上的路徑,更加完整、好讀。
本來是這樣:
@router.get(path='/{post_id}/') # 前綴路由在專案一級路由定義了
現在是這樣:
@router.get(path='/posts/{post_id}/') # 全部改在 app 的二級路由定義
注意,這是為了提升教學體驗,工作中我們通常不會這麼做,不然就失去了模組化路由的優勢。
改動二:新增 API#
為了示範 path parameters,我們必須有一個實踐這個功能的 API。
我們新增一個「取得單一文章資訊」API。
好,先這樣,我們可以開始了解 path parameters 了。
以下程式碼皆取自於範例專案。
三、在 Django Ninja 中使用 Path Parameters#
在 Django Ninja 中,定義路徑參數非常簡單。透過 router 裝飾器與 type hints,我們可以輕鬆處理這些參數,自動進行類型轉換。
定義「帶有路徑參數」的 API#
讓我們看看如何在 Django Ninja 中定義一個帶有路徑參數的 API:
@router.get(path='/posts/{post_id}/')
def get_post(request: HttpRequest, post_id: int) -> Post:
post = Post.objects.get(id=post_id)
return post
範例中,{post_id} 是一個路徑參數,整個 path 字串將被解析(parsing)並傳遞給 get_post 函式中的 post_id 參數。
Django Ninja 會依照函式簽名中定義的類型,自動進行型別轉換。
例如,我們在 view 函式中標記了 post_id: int,Django Ninja 會自動將來自 URL 的字串參數轉換成 int。
換言之,處理 path parameters 的流程,同時具有兩種效果:
- 參數型別驗證:避免前端傳來
post_id內容是錯誤的型別,而在 view 函式內部還繼續嘗試處理這個值,直到發生錯誤。 - View 函式內部的自動型別轉換:省下在函式內自行轉換的功夫。
四、與 Django 原生 Path Converters 兼容#
Django 在處理 URL 時,本來就提供了「path converters」來讓你對請求路徑進行「嚴格配對」。
配對成功,才會把 HTTP 請求「轉發」給特定的 view 函式。
這裡的「嚴格」,指的是型別要與 path converter 所定義的相符,才能夠配對成功。
常見的 converters 類型包括 str、int 和 slug,它們能夠限制 URL 中參數的格式:
path('posts/<int:post_id>/', views.get_post),
<int:post_id> 就是一個 path converter,它要求 post_id 必須是一個整數。
值得強調的是,path converters 的主要目的不是為了型別轉換——這只是附帶的。而是為了端點路徑的「模式比對」(pattern matching)。
在模式不符合的情況下,根本不會配對成功,當然也不會進行轉型。
Django Ninja 中的 Path Converters#
在 Django Ninja 中,這些原生的 path converters 仍然可以使用,而且進一步簡化了。
直接寫在 router 裝飾器的路徑字串中即可:
@router.get(path='/posts/{int:post_id}/')
如前所述,有了 path converter 後,如果 post_id 不是有效的 int ,則 URL 模式配對將直接失敗——請求不會進入 view 函式,更不會有型別轉換。
如果沒有其他路徑被成功配對,Django 將直接返回「404 Not Found」。

個人認為,在 Django Ninja 中,path converters 的功能已部分被 type hints 所取代。如果要同時使用 path converters,須留意兩者的判斷順序(path converters 先判斷)與兩者設定的型別一定要相同。
五、請求的基本錯誤處理#
當請求中的路徑參數不符合 type hints 定義的型別時,Django Ninja 會自動返回一個帶有錯誤訊息與提示內容的 HTTP 回應,狀態碼為 422。
比如說,如果用戶請求的路徑是 /posts/abc/(post_id 參數沒有給數字),將得到下列回應:
// http://127.0.0.1:8000/posts/abc/
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"post_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer"
}
]
}
這樣的自動錯誤處理機制,不僅提高了 API 的穩定性,同時簡化了開發者的錯誤處理邏輯。
內建的 422 回應在 Django Ninja 中極為常見,省下了我們不少時間。
小結與下一步#
路徑參數是 RESTful API 中的重要組成部分,Django Ninja 透過 type hints 和自動化的錯誤處理,讓我們能夠輕鬆處理路徑中的動態參數。
此外,它與 Django 原生的 path converters 保持了良好的相容性,提供高效、簡潔的開發體驗。
下一篇,我們將深入探討查詢參數(query parameters),說明如何在 Django Ninja 中處理這些參數,進一步提升 API 的靈活性與功能。