回應(一)Django Ninja 處理 HTTP 回應
2024 iThome 鐵人賽
這是 Django Ninja 系列教學的第 13 篇。
這一篇要正式進入「HTTP 回應」環節,也就是第三小節。
本節將透過 4 篇文章,介紹 Django Ninja 如何處理 HTTP 回應:
- 卷 13:回應(一)Django Ninja 處理 HTTP 回應(本文)
- 卷 14:回應(二)用 Schema 建立巢狀結構回應
- 卷 15:回應(三)為何不用 ModelSchema?——相比 DRF,我更偏愛 Django Ninja 的理由
- 卷 16:回應(四)Resolver 方法——欄位資料格式化
我們會講述更多 Schema 用法,透過這些技巧,你能夠精確地控制 API 的輸出格式。無論是單一物件回應,還是複雜的嵌套結構,接下來都會一一提及。
本文所有的程式碼變動,可參考這個 PR。
本文將一步一步,從簡單到複雜,介紹如何透過 Django Ninja 建立 HTTP 回應。
並且用既有的 3 個 API 進行示範(會依需求為它們增補不同內容):
- 新增文章:示範簡單回應,加上狀態碼。
- 取得單一文章:示範單一物件回應,需要 Schema 與定義
response=
參數。 - 取得文章列表:示範多個物件回應。
開始吧!
一、簡單回應:新增文章
先來看最簡單的回應格式,這個例子會展示如何回應一個 Python 字典,並手動設定 HTTP 回應狀態碼。
以「新增文章」API 為例:(省略部分程式碼)
1 |
|
這裡回應的是一個 Python 字典,事實上,你可以 return「任何能夠 JSON 序列化」的 Python 資料。(所以 Django 模型物件不行,因為它無法直接序列化)
因此,以下這些都可以 return:
- 單純的字串:
"Hello World !"
- Python list:
[1 , 2 , 3]
- 巢狀的資料結構:
{"name": "Alice", "age": 30, "hobbies": ["reading", "swimming"]}
這些都會被 Django Ninja 自動序列化為 JSON 格式,並作為 API 的回應:
1 | { |
為回應加上 HTTP 狀態碼
View 函式處理回應,往往要加入 HTTP 狀態碼。尤其在有多種回應狀態的時候,需要透過狀態碼來區分。
做法很簡單,就是在回應的內容前面直接加上:
1 | return 201, {'id': post.id, 'title': post.title} |
如此一來,函式的回傳型別就從原來的dict
變成tuple
了。
所以我們函式簽名的 type hints 也要跟著修正:
1 | def create_post(...) -> tuple[int, dict]: |
如果你沒有加前面這個狀態碼數字,Django Ninja 就將其預設為 200。
值得注意的是,當你的 view 函式要 return「非 200」回應時,必須在router
裝飾器聲明:
1 | # 這裡 |
response={201: dict}
就是聲明的方式,採用 Python 字典來一一對應狀態碼與回傳內容格式。
創作當時,這部分的範例專案程式碼還未補上,所以這個 API 無法正常回應😅,特此提醒。
上述第一種回應很簡單,不過大部分 API 回應都沒這麼單純。
我們來看第二種回應。
二、單一模型物件回應:取得單一文章
開發 Django API,回應中的資料,有很大部分是從 Django 模型物件序列化而來。
但通常我們不會直接將資料庫中的所有資訊傳送給前端。相反,我們會進行欄位篩選、驗證或格式轉換。
這樣不僅能夠精確控制 API 的輸出,還能確保資料的正確與安全性。
Django Ninja 中,這些「篩選、驗證、格式轉換」等需求,都是透過 Schema 實現。
我們來為「單得取一文章」API 設計一個回應格式,使用 Schema。
1 | # post/schemas.py |
這個PostResponse
Schema 包含了Post
幾乎所有的欄位。
注意,Schema 定義將決定輸出的欄位。如果 Schema 中只有id
一欄,那輸出結果就只會有該欄的資料。
接著,我們在 view 函式中使用這個 Schema:
1 |
|
只有改一行!——在router
裝飾器加上response=PostSchema
。
有了response=PostSchema
設定,Django Ninja 會將函式回傳的Post
模型物件,丟給PostSchema
進行驗證,成功之後直接轉為 JSON 格式並送回前端。
看看回應結果:
1 | // http://127.0.0.1:8000/posts/2/ |
非常好!
三、多個模型物件回應:取得文章列表
「清單、列表」也是 API 的常見回應形態,包含多筆資料。
我們繼續使用剛剛的PostSchema
,不作任何更動,直接套用在「取得文章列表」這個 API。
一樣,只要更改一行即可,但與前面略有不同:
1 |
我們使用了list[PostSchema]
,表示回應會是一個PostSchema
物件的 list。
Django Ninja 自動處理 Iterable
然而實際上,此時你不需要「真的」return 一個 Python list,可以直接回傳 QuerySet 就好,Django Ninja 會自行處理物件的迭代與序列化。
甚至,只要你 return 的是一個 iterable,而且 iterable 中的每一個元素,都能夠通過PostSchema
驗證(符合格式),那就足夠了!
來看看結果,因為列表太長了,我改用截圖呈現:
API 回應:取得文章列表
多重狀態碼回應
上面提到的回應,不是 200 就是 201,但通常 API 往往還會有 400、401、403 甚至 500 等回應,如何處理它們之間的對應關係?
沒錯,就是擴大response=
中的字典!我們直接看官方文件的例子:
1 | class Token(Schema): |
值得留意的是,字典的 key 不可重複,但值可以!——Message
出現了兩次。
但我覺得這個「多重狀態碼回應」設定在實務上沒有很實用,為何?我們後續再談。
小結
本文中,我們從最簡單的回應開始,逐步介紹了如何在回應中返回單一和多筆資料,並提到了 Django Ninja 如何設定多重狀態碼回應。
下一篇將探討,如何處理回應中複雜的巢狀結構,讓我們的 API 愈來愈健全。