昨天晚上突然想到,使用 Postman 測試 [ PATCH ] api/products/{product} :修改單一產品部份資料 這支 API 時,我有遇過一個問題,所以今天要來分享一下。
這個問題我自己覺得蠻重要的,也許有跟我一樣的小夥伴遇過這樣的問題。(還是只有我XD)
當使用 Postman 發送 PATCH 請求來更新產品名稱時,請求內容如下:
{
"product_name": "古早味紅茶"
}
但伺服器回應如下:
{
"message": "商品資料更新完成!",
"product": {
"id": 3,
"type_id": 2,
"product_name": "杏仁瓦片",
"product_description": "600ml,阿嬤的古早味,最對味!",
"price": 40,
"created_at": "2024-09-30 09:37",
"updated_at": "2024-09-30 09:37"
}
}
雖然顯示更新成功訊息,但卻顯示產品名稱並未更新。
菜雞仔如我,當時特別震驚,不知道為什麼發生這樣的事情(皺眉)
深呼吸後,就先來找出問題來源,我們來 log log 吧!
必須說一下,我當時真的很菜,我還不太會使用 log 去查看錯誤日誌,當然也不會什麼 dd 或其他方式,我直接丟 GPT 詢問找出問題的方法,這是 GPT 給的其中一個建議,後來認真理解後,用 log 記錄這個方法在往後真的幫助我好多好多~(拭淚)
\Log::info()
來記錄日誌在 Laravel 框架中,Log
是一個很好用的日誌系統,讓開發者可以方便地記錄應用程式運行時的各種資訊。
Log 提供了多種方法來記錄不同層級的日誌,例如 info、warning、error 等等。
其中 info
用於記錄一般的資訊性訊息。
這些記錄通常位於 storage/logs/laravel.log
。
當你在 Laravel 裡用 \Log::info()
的時候,前面那個反斜線 \ 就像是在告訴 PHP:「我要的是全域的 Log,那個 Laravel 預設的 Log,不是其他的!」
因為 Laravel 的程式碼通常都在不同的命名空間裡(例如 App\Http\Controllers 這種分類),所以如果你直接寫 Log::info()
,PHP 可能會以為你要找的是這個命名空間下的 Log,結果它可能找不到,然後報錯。
簡單來說,反斜線就像是在導航一樣,加了反斜線以後,就不會遇到命名空間搞混的問題。
例如:更新產品名稱
先驗證需要更改的名稱是否符合我自訂的規則,符合的話便更新,更新後便回傳更新後的完整資料。
\Log::info()
在每一個步驟記錄public function update(Request $request, Product $product)
{
// 記錄原始請求資料
\Log::info('Raw request data: ', $request->all());
// 驗證規則
$rules = [
'type_id' => 'sometimes|integer|min:0',
'product_name' => 'sometimes|string|max:255',
'product_description' => 'nullable',
'price' => 'sometimes|integer|min:0',
];
// 驗證請求資料
$validatedData = $request->validate($rules);
// 記錄驗證後的資料
\Log::info('Validated request data: ', $validatedData);
// 更新產品資料
$product->update($validatedData);
// 強制同步資料庫
$product->refresh();
// 記錄更新後的產品資料
\Log::info('Updated product: ', $product->toArray());
// 返回更新後的資料
return response()->json([
'message' => '商品資料更新完成!',
'product' => $product,
], 200);
}
查看 laravel.log 時,發現 Raw request data
是空的,這表示請求的資料從一開始就未正確傳遞。
檢查 Postman 設定後,確認沒有設定 Headers : Content-Type:application/json
,僅有設定:Accept: application/json
。
當時,我對於 Header 並沒有很了解,基本上只有照著基礎操作文章設定而已,沒有去認真理解我設定的東西是什麼。
所以在這邊發生問題時,在查找問題的過程中又更加對某些部份有所了解,果然經驗都是累積的!(插腰點頭)
那什麼是 Content-Type 跟 Accept 呢?
以下整理 GPT 的回覆:
在進行網路請求時,Content-Type 和 Accept 是兩個經常會遇到的重要 Header。它們雖然看起來類似,但功能完全不一樣。
是用來告訴伺服器或接收端:「我發送給你的資料是什麼格式」。
這樣,接收端就可以依照這個 Header 來正確處理和解析資料。
例如:
如果你傳的是 JSON 格式的資料,你會在請求的 Content-Type 裡寫上 application/json。這樣伺服器就知道你傳的資料是 JSON 格式,它就會依照這個格式去解析你的資料,確保資料能被系統理解。
是用來告訴伺服器:「我希望接收到的回應是什麼格式」。
當你發送請求時,會告訴伺服器:「我想要你回傳 JSON 格式的資料」,那伺服器就會依照你的要求,把回應的資料格式化成你希望的樣子。
例如:
當你設定 Accept: application/json,就是告訴伺服器:「我想要 JSON 格式的回應哦!」,伺服器收到這個請求後,會試著把資料回傳成 JSON 格式。
建議能參考這篇文章:學習筆記 | 什麼是網路請求(HTTP request)
我覺得這篇文章寫得很易懂!
使用 Postman 測試時,我沒有告訴它發送的資料是什麼格式,也就是沒有設定Content-Type:application/json
http://127.0.0.1:8000/api/products/3
PATCH
Accept: application/json
Content-Type: application/json
Body: 選擇 raw 並設定格式為 JSON,輸入以下內容:
{
"product_name": "古早味紅茶"
}
{
"message": "商品資料更新完成!",
"product": {
"id": 3,
"type_id": 2,
"product_name": "古早味紅茶",
"product_description": "600ml,阿嬤的古早味,最對味!",
"price": 40,
"created_at": "2024-09-30 09:37",
"updated_at": "2024-09-30 12:48"
}
}
正確設定 Content-Type
後,再次發送請求,已正確更新產品資料並回傳更新後的產品資料。