ALPHA Camp畢業專案-Simple Twitter之解決問題與工具運用

AC 學期三 Twitter專案協作中解決問題的經驗

  • 規格說明:使用者能編輯自己的名稱、自我介紹、個人頭像與封面
  • 我在實作中的角色:開發後端API、路由建立

PR 網址


情境

這個需求是整個專案的指定功能中,唯一需要與「上傳檔案」有關的功能。根據之前全端開發並部署 heroku 的經驗以及 model 的設計,著手前就與後端隊友有共識:使用者的個人頭像與封面圖片的上傳,是要先上傳至第三方圖片空間imgur,拿到 imgur 回傳的網址後,以 string 的型態存入資料庫。
由於使用者編輯表單內容完成按下儲存後,前端需要即時顯示更新後的資料,故後端除了更新資料之外,也需要回傳一包新的資料給前端。

溝通

前端隊友表示會將夾帶檔案的表單資料以 FormData 的格式傳入後端,剛開始我們對 FormData 格式沒什麼頭緒,所以先請前端隊友提供相關教案與資料參考,初步了解「FormData就是把整個 <form> 的內容轉換成一個物件,把整個物件傳送給後端」,這樣聽起來是沒什麼問題的。

嘗試

使用imgur服務「將圖片上傳 imgur,接收網址回來」這段實作與之前經驗大同小異,沒有遇到困難。與先前全端開發不同的是,這個階段沒有辦法直接從前端送一個表單資料回來測試。經由隊友提示在 Postman 中,可以將req.body設定為 form-data,可以測試上傳圖片的功能。

Image

但此時不管怎麼打,return 出來的東西都是空空的。

由於此條路由是「帳戶設定」(帳戶、名稱、信箱、密碼修改)與「編輯個人資料」(名稱、自介、大頭照、封面修改)共用,controller 要處理很多個 input value,用 console.log 大法仔細檢查,最後發現原因出在req.body竟然是 undefine!

除錯

試著在 Postman 改回 raw: JSON格式輸入文字資料(例如:帳戶、名稱等非圖片的欄位),印出req.body以及 response 皆正常,這說明 CRUD 的部分是正常運行,故問題還是出在req.body中。靈光一閃想起在 app.js 主程式中,已經設定每條路由進來會經過 Express 內建的 body-parser 解析資料,並且把輸入的資料放入req.body,json 格式可以,但 form-data 格式不行!難道是要請前端改為 json 格式傳入嗎?


所以到底什麼是 form-data?

HTTP 回應中,會使用 Content-Type 來表示媒體類型(或稱 MIME type),Content-Type 的資訊會存在 HTTP Header 中。

The Content-Type representation header is used to indicate the original media type of the resource (prior to any content encoding applied for sending). (MDN)

常見的有:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
  • application/json

FormData 的資料類型全名是 multipart/form-data,通常表單的預設格式為 x-www-form-urlencoded,會把表單資料轉成 key=value 格式。若表單含有檔案寫入時,須將把格式改成這個 form-data,才能順利地把圖片一起 POST 進去。

而 body-parser 一開頭就寫了不支援 multipart bodies,所以當前端傳送 FormData 時,需要額外進行處理。

This does not handle multipart bodies, due to their complex and typically large nature. (Express: body-parser)


解決

Multer 可作為 Node.js 處理 multipart/form-data 格式的 middleware,常用於上傳檔案的情境(readme)。
會選擇 Multer 的原因為:(1) 查到的相關技術文章是以 multer 作為解決方法,仔細閱讀後發現做法看起來容易,不複雜、(2) Multer 在 npm 上一年內的平均每週下載量為兩百八十多萬次、套件上次更新發行時間是兩個月前,顯然是一個目前熱門被使用、也有持續在更新的工具。

接下來就是仔細照著 Multer readme 內容,一步步新增 middleware 與修改 route,就成功拿到正常的req.body,Postman 測試也十分順利,又解決了一個問題!