## DRF 的 Response 與 Django 的 JsonResponse:「隨手用」的背後真相 {#sec-415c48e5bc9d} 與大多數 Django 開發者一樣,我在 [[Django]] 專案中幾乎 99% 都會搭配使用 DRF ([[Django REST Framework]]) 套件。因此,當向伺服器返回回應時,無論該回應是 API 回應,還是模板中請求的簡單 JSON 請求,我都會不假思索地使用 `Response` 這個結構化的回應類別。我真的就是「隨手用」而已。 因為它不僅與序列化器(Serializer)配合得天衣無縫,而且在所有回應中預設使用 `Response` 類別,就無需再費心思考。 然而,今天我突然想到:「**話說回來,Response 到底與 django.http.JsonResponse 有什麼不同?差別又在哪裡呢?**」因此,今天我將嘗試解答這個疑問。 --- ### **1. 根源不同:靜態 (Static) 還是彈性 (Flexible)?** {#sec-2451e29dd2a3} ![JsonResponse和Response的差異圖示](/media/whitedec/blog_img/66a44f9db4b44419a1a34d72608f8eb9.webp) 首先,我查閱了這兩者的「血統」,發現了一個有趣的差異。 * **JsonResponse:** 繼承自 Django 的 `HttpResponse`。它的目的非常明確:「接收數據,然後透過 `json.dumps()` 進行序列化,再將 Content-Type 設定為 `application/json` 並發送出去。」就這樣,一個非常簡單直觀的類別。 * **DRF Response:** 它繼承自 `SimpleTemplateResponse`。嗯?這是什麼意思?為什麼會繼承與模板相關的類別呢?從這裡開始,`Response` 的真實身份逐漸浮現。原來我一直隨手使用的這個類別,是一個在最終結果尚未確定之前的**「渲染前數據容器」**! --- ### **2. 核心差異:內容協商 (Content Negotiation)** {#sec-4b3b34038277} `JsonResponse` 就像是個「非 JSON 不可」的固執傢伙,而 DRF 的 `Response` 則非常靈活且聰明。 DRF 的 `Response` 會詢問客戶端想要什麼。(這就是所謂的 **Content Negotiation**,內容協商。) 如果客戶端在標頭中發送 `Accept: text/html`,它會顯示我們熟悉的漂亮「Browsable API」介面;如果發送 `Accept: application/json`,則只會發送純粹的 JSON 數據。 也就是說,`Response` 類別本身僅用於承載數據,而實際以何種形式呈現則由 DRF 的 **Renderer** 決定。我們之所以能夠隨心所欲地使用 `Response`,並獲得符合情境的回應,正是因為 DRF 在背後努力進行了協商。 這真是一個令人驚訝的發現與領悟。我不禁感嘆,這工具設計得真是精密。每當遇到這樣的時刻,我都會對 Django/DRF 的開發者和貢獻者們心懷數秒的感激。 --- ### **3. 序列化 (Serialization) 的便利性** {#sec-f361c5e0b07e} 當使用 `JsonResponse` 時,我們需要手動將數據「整理」成 Python 字典或列表的形式才能傳遞。相比之下,`Response` 與 DRF 的 Serializer 簡直是天作之合。 讓我們來看一個簡單的例子。如果需要返回部落格文章資訊,會是怎樣呢? **案例 A: JsonResponse (手動處理版本)** ```python from django.http import JsonResponse def post_detail(request, pk): post = Post.objects.get(pk=pk) # 必須手動將數據一個個建立成字典。 # 如果有 20 個欄位呢?嗯... 可能會有點惱火。 data = { "title": post.title, "content": post.content, "created_at": post.created_at.strftime("%Y-%m-%d"), # 日期格式也要手動處理! } return JsonResponse(data) ``` **案例 B: DRF Response (自動化版本)** ```python from rest_framework.response import Response from rest_framework.decorators import api_view @api_view(['GET']) def post_detail(request, pk): post = Post.objects.get(pk=pk) serializer = PostSerializer(post) # 直接丟出 .data 即可。 # 複雜的關聯(Foreign Key)或日期格式化都由序列化器自動處理。 return Response(serializer.data) ``` 看到了嗎?`JsonResponse` 需要我們親自處理食材並擺盤,而 `Response` 則可以將複雜的模型實例或查詢集放入 Serializer 這個「自動烹飪機」中,然後直接丟出結果。因為 Renderer 會自動將其轉換為漂亮的 JSON 字串。 有時在與像 ChatGPT 這樣一絲不苟的 AI 合作時,我曾多次遇到它出於過度熱心或過度防禦,而逐一建立回應數據,最終才呼叫 Response 返回的情況。注重效率的開發者看到這樣的程式碼可能會感到有些不適。現在,如果 AI 產生了這樣的程式碼,我們就該果斷地修正它。 --- ### **那麼,即使是簡單的 JSON 回應,也應該繼續使用 Response 嗎?** {#sec-ff7e587ad4e3} 這是我 100% 基於個人主觀經驗的結論,至少我的回答是:「**完全沒問題。甚至建議這麼做。**」 當然,從技術上來說,`Response` 會經過內容協商 (Content Negotiation) 過程並檢查多個 Renderer,因此可能會比 `JsonResponse` 多出非常微小的運算量。然而,在現代基礎設施(即使是我在 Raspberry Pi 5 上運行!)中,這種差異幾乎難以察覺。 相反地,持續使用 `Response` 所帶來的好處遠遠更大。 1. **一致性:** 可以在整個專案中統一回應格式。 2. **除錯:** 透過瀏覽器存取時,可以方便地透過 Browsable API 直接查看數據。 3. **彈性:** 未來如果需要將回應格式擴展為 XML 或 YAML,無需修改程式碼,只需透過配置即可應對。 --- ### **結語:了解差異後,豁然開朗** {#sec-b1ff40748b2c} 透過今天的學習,我得出了這樣的結論: > **「歸根結底,既然沒有顯著的性能差異,在 DRF 環境下,繼續沿用 Response 對於心理健康更有益處。」** 然而,當我了解了每天使用的工具內部是如何運作的,以及為什麼必須使用 `Response` 而非 `JsonResponse` 這個獨立的類別時,感覺真是豁然開朗。果然,開發者只有不斷追問「為什麼?」,才能更進一步成長。 現在,既然已經了解了它的本質,我就能更自信、更有效地使用 `Response` 了。 --- 如果有人也曾像我一樣,因為 Django 和 DRF 之間的細微差異而輾轉難眠,希望這篇文章能對您有所幫助。 如有任何疑問或意見,歡迎在下方留言! 如果這篇文章對您有幫助,請點擊追蹤!您可以在 [Mikihands Blog Flatform](https://blog.mikihands.com/) 註冊帳號後進行追蹤。在部落格撰寫文章並有人閱讀的體驗... 相當有趣。