Expense-Tracker 專案筆記

William Tsou
8 min readAug 10, 2021

紀錄實作 AC 三學期 私房錢專案的過程

一、為何選擇實作這項專案?

主要是希望透過一項專案來練習使用 node.js 後端執行環境搭配 express 框架開發網頁,體會從無到有建立一個網頁的流程。

除此之外,也有特別想要練習的功能,例如使用者認證功能、依據選項篩選資料的功能和透過 ODM 操作資料庫的能力…等,所以選擇建立一個紀錄支出的網頁專案,因為它可以讓我練習到上述的功能,規模又不會太過複雜,以免因為卡關太久,反而降低了學習效果。

二、使用了什麼技術?

這項專案使用了 node.js 搭配 express 框架,也有使用到許多 npm 上的第三方套件去達成特定的功能,整體專案採用 MVC 的架構編成。

express-handlebars : 我選擇 hbs 作為樣板引擎,它可以用類似 HTML 的格式,讓我可以從伺服器端去渲染網頁畫面,對應到 MVC 中 Views 的部分。

bootstrap : 開源的 CSS 框架,因為這項專案的主要目的是練習後端開發環境和使用者認證功能的邏輯,所以希望藉由 bootstrap 加快前端頁面的生成,這樣比較可以專注在後端開發上。

handlebars-helpers : 由於 handlebars 內建的函式不足以應付特定需求,我選了引入 handlebars-helpers 的第三方套件,協助我完成更進階的效果,例如使用 {{#is}} 來比對值。

mongoose : 與資料庫 MongoDB 搭配使用的 ODM ,可以直接在伺服器端操作資料庫,實作出基本的 CRUD 功能。

express-session : 使用者驗證的功能中,為了透過 cookie 實作 session ,將無狀態的 HTTP 轉換為可識別的狀態,所以採用此套件實作和存放 HTTP requests 的 session 資料,並搭配 passport.js 驗證使用者的身分。

passport.js :透過知名的 passport.js 套件,驗證特定請求是否為登入狀態,並且設定本地驗證、 facebook 驗證和 google 驗證三種形式,以此實作出登入、登出來切換使用者狀態的功能。

connect-flash : 為了在註冊、登入時,將伺服器端回傳的錯誤訊息渲染在網頁頁面上,讓使用者知道錯誤發生的原因,以便增進使用者體驗,所以採用 connect-flash 套件設定提示訊息,並且搭配 handlebars 的 partial message 渲染快閃訊息。

bcrypt.js : 雖然只是一個小專案,但直接將使用者的密碼以明碼形式存入資料庫,並不符合資安的規範,通常會需要將輸入的密碼作雜湊處理後再存入資料庫,因此引入 bcrypt.js 替使用者註冊的密碼雜湊,也一併修改驗證策略中比對密碼的程式碼,改為用雜湊後的亂碼去比對。

dotenv :由於呼叫第三方 API 時,需要設定 API_KEY 等敏感資訊,但又不能直接寫死在程式碼的邏輯中,以免誤將敏感資訊上傳至公開的環境,所以本專案使用了 dotenv,將串接 facebook 與 google 的 API_KEY 等參數設置於環境變數中。

三、哪部分你相對能掌握?哪裡花了最多時間?

在建置整個專案的過程中,實作出基礎的 CRUD 功能及符合 RESTful API 風格的路由,是個人認為比較能迅速掌握的部份,過程中基本上沒有遇到卡關的狀況,可以順利的呼叫出預期的資料,並且依據路由作出對應的處理。

花費時間最久的部分,是本次專案練習的重點:使用者認證功能,由於對整個 session 機制還不是這麼熟悉。從伺服器接收請求到回傳資料渲染頁面的這段過程中,使用者的狀態是如何驗證、如何依據使用者的狀態去顯示不同的樣板,以及顯示快閃訊息提示使用者的相關邏輯…等,都花費了蠻大的心力與時間去完成。

預期的快閃訊息畫面

個人覺得主要的原因在於對整個流程的概念不夠清楚,所以當程式碼沒有按照預想中的方式執行時,很難迅速地判斷出錯的環節是路由、驗證策略還是其他意想不到的地方,也因此花費了很多時間在與錯誤無關的資訊、概念上打轉。相較於前述的 CRUD 基本功能實作,雖然過程中也有遇到一些問題,但通常可以立刻發現錯誤,可見對使用者驗證的概念還需要更加熟悉,才能加快鎖定錯誤關鍵的速度,加快開發效率。

四、過程中碰到什麼困難?又如何克服?

在實作提示使用者的快閃訊息時,雖然可以在登出、註冊的路由中,透過 req.flash() 設定回傳的快閃訊息,卻不知道如何取得 passport.js 本地登入驗證中設定的快閃訊息,並且顯示在登入頁面上。

我一開始是直接去 Stack Overflow 輸入關鍵字查詢相關的資料,但由於下的關鍵字可能不夠精準,找到的解答反而讓我更混淆。所以,我決定去 connect-flash 和 passport.js 的官方文件中尋找解答,後來發現了兩種做法。

第一種是透過在 passport.js 的驗證中,加上 failureFlash : true 的參數,這樣一來 passport.js 就會將 done 物件內傳入的 message 參數 ( 第三個 ),轉換成 req.flash(‘error’, ‘message’) ,接著便能透過 req.flash(‘error’) 取出快閃訊息的內容了!

第二種方法則是在 passport.js 驗證中加上 failureMessage : true 的參數,並且同樣在 done 物件內傳入 message 參數 ( 第三個 ) ,就可以直接在 req.session.messages 取得快閃訊息的內容了。

當然可能還有其他的作法,但基於時間考量,我決定採用第一種 failureFlash 的作法,將其值指定給 warning_msg 後,也成功在登入頁面顯示出快閃訊息。

router.post(‘/login’, passport.authenticate(‘local’, {  successRedirect: ‘/’,  failureRedirect: ‘/users/login’,  failureFlash: true}))

另一個遇到的困難是將種子寫入資料庫時,由於伺服器端的 JS 程式碼在同步執行的狀態時,只要成功呼叫創建資料的函式後,就會繼續執行後續的程式碼,而不會等待資料庫回傳創建完畢的訊息。

因此,會發生伺服器端已經印出創建完畢的訊息,在資料庫卻找不到半筆種子資料的詭異情況。為了解決這個問題,我花了一些時間研究 Promise 和 JS 同步非同步的觀念。

最後決定使用 Promise.all() 搭配 Array.from() 一口氣在伺服器創建多筆資料,同時確保它們都創建成功後,在使用 .then() 作後續處理。

五、過程中你有對哪個技術有特別深刻的學習?

在開發這份專案的過程中,我最深刻學習的是使用者驗證功能,從路由、使用者資料的設計,到引入第三方套件實現 session 、驗證登入狀態、雜湊加密和第三方登入…等功能,甚至是環境變數的設定也有涉略。

我不僅學到了現代網頁非常常見的註冊、登入和登出功能背後大致上的邏輯,也理解了 session 的機制是為了辨識請求來自哪個使用者,因此常見的做法就是直接在客戶端的 cookie 儲存資料,讓使用者發出請求時一併夾帶。

但也並非只有 cookie 這種作法,也有只發送一組 session-key 給客戶端,然後將使用者的資料都儲存在伺服器端,並依據 session-key 去搜尋是否有符合的使用者資料。再加上之後還會學到 token-based 的驗證機制,由此可見 cookie 只是實作 session 的手段之一,這也讓我更清楚 cookie 與 session 的差別。

自己畫的使用者認證流程示意圖

至於驗證的功能,主要是透過第三方套件 passport 來達成,而且可以引入不同的驗證方式,並在路由的地方設定驗證方法,例如登入頁面發出的 POST 請求就要加上本地驗證,FACEBOOK 登入按鈕引導的頁面則是加上 facebook 頁面。 除此之外,也要加上一層驗證的中介軟體,並在需要登入狀態才能使用的頁面路由前方加上該驗證器,將未登入的使用者回導至登入頁面。

此外,雜湊與環境變數的設定,也提醒我資安相關的議題,雖然還只是十分基礎的部分就是了。使用者註冊後,不能直接將密碼以明碼的形式儲存在資料庫內,而是得將雜湊後的亂碼儲存進去,並在使用者登入時,將雜湊後的亂碼比對。

而之所以不能將明碼存進資料庫內,是為了避免工程師可以直接看到使用者的密碼外,也是避免資料庫遭到攻擊外洩後,直接曝光使用者的密碼。難怪每次忘記密碼的時候,絕大多數的網站都會要我重新設一組新密碼,而不是確認身分後告訴我舊密碼,因為伺服器也不知道我的舊密碼是什麼啊XD

而環境變數的設定則是為了避免 git commit 時,直接將重要的敏感資訊一起 commit 出去,而且 commit 的訊息還是無法消除的,所以要將金鑰、密碼之類的設定放到環境變數的檔案中,並且在 .gitignore 檔案中加上該檔案,避免 git commit 時將該檔案一併送出去,等於白忙一場。

以上就是本次專案的心得筆記,因為這次學習的內容相當龐雜,但透過心得筆記重新回顧後,除了可以再次複習學到的觀念外,也會發現有些自以為熟悉的觀念,卻在文章寫到一半時卡住無法下筆,有助於自己釐清到底還有那些地方的觀念不夠清楚!

--

--

William Tsou

紀錄在alphacamp學習的點點滴滴,以及邁向前端工程師的沿途風景