[Node.js] Express
什麼是 express
觀看 Node.js 上一文章 server.js 的 code,在那樣的情況下編寫不算太困難,但如果開始添加更複雜的路徑或是處理表單… 等,要添加更多的伺服器邏輯,那麼它將會變得相當混亂,難以管理。
而 Express 是一個能幫助我們更輕鬆管理該情況的框架,它能使我們的 code 更容易閱讀、更新和擴增。
首先透過 npmjs.com 搜尋 express,看到安裝的部分
這裡筆者開啟一個空的專案,並在終端輸入 npm init
指令,初始化專案並產生 package.json 檔案
接著輸入安裝 express 的指令 npm install express
透過 package.json 的 dependencies 確認已安裝 express
建立 express app
可以看 express 官方文件學習如何使用 express,以下我們直接示範,並與之前的 server.js 做比較
在專案建立一個 app.js 檔
在 app.js 輸入以下 code
1 | const express = require('express'); |
以上表示 express 模組回傳一個函數,而我們將該函數存儲在 express 這個常數,接著調用該函數回傳一個物件並存儲在 app。
之後的操作都要藉由 app 物件來完成,就像之前的 server 物件。
在 server.js 監聽伺服器使用 listen() 方法,express 也有,表示監聽端口號 3000 發出的請求
1 | app.listen(3000); // listen for requests |
現在如果想要監聽 get 方法的請求,使用 app.get()
參數有兩個,第一個為想監聽的 URL,就像是之前 switch 中的各個 case,第二個參數為 callback function,包含了 req、 res 兩個物件參數,讓我們可以像之前一樣處理請求以及響應的物件
1 | app.get('/', (req, res) => { |
接著使用 res 物件處理響應的部分,可以與 server.js 一樣使用 res.write()
及 res.end()
,但這裡我們使用 express 的方式 res.send()
,可以直接在其中輸入要發送的內容
1 | app.get('/', (req, res) => { |
使用 res.send()
的好處是它會先推斷出我們打算響應給瀏覽器的內容類型(Content-Type),它會自動的為我們設置內容類型標頭,也就是我們不需要手動設置
1 | res.setHeader('Content-Type','text/html'); |
另一個好處是它會為我們自動判斷狀態代碼(status code),像這裡我們執行將發送 HTML 給瀏覽器,狀態會是 200。
實際執行
全部的 code
1 | const express = require('express'); |
這裡使用 nodemon 執行
一樣連上 localhost:3000
顯示畫面
打開開發人員工具檢查狀態為 200
Content-Type 也自動設置為 text/html
Routing & HTML
現在試著連結 localhost:3000/about
結果顯示如下,是因為我們沒有針對 /about 做響應
只要像前面做的一樣,使用 app.get()
對 /about 做處理即可
1 | const express = require('express'); |
執行,再次連上 localhost:3000/about
但我們不可能用這樣的方式傳一個 HTML 頁面,而是建立一個單獨的 HTML 文件,server.js 使用 fs 檔案系統模組來發送這樣的一個 HTML 文檔,這裡則不需要 fs 模組也能做到。
首先將之前專案的 views 複製一份到該專案,裡面包含 index.html、about.html、404.html
接著使用 res.sendFile()
,第一個參數為檔案路徑,但由於使用相對路徑,所以必須加上第二個參數告訴 express 根目錄,如此才能知道檔案路徑是相對於何者,這裡第二個參數將根目錄設為 app.js 的目錄路徑也就是 'D:/Desktop/NODE-TEST'
,使用 __dirname
表示
1 | app.get('/', (req, res) => { |
但也可以將第一個參數使用絕對路徑便不需設置 root,但筆者不推薦,因為這僅限於在自己的電腦上運行,若是將 code 託管到別的主機便無法正常運作
1 | app.get('/', (req, res) => { |
將 /about 也使用同樣方式設置 about.html
1 | const express = require('express'); |
執行,連接 localhost:3000
,顯示 index.html
連接 localhost:3000/about
,顯示 about.html
接著我們可以在 index.html 及 about.html 都添加導覽列
index.html
1 | <!DOCTYPE html> |
about.html
1 | <!DOCTYPE html> |
這樣就不用在網址輸入切換,直接點擊連結就好
Redirects & 404 pages
前面說明了怎麼監聽請求、響應 HTML 頁面,這些都在 server.js 做過,接著再來說說 express 在重新定向及 404 頁面的部分。
這裡我們將設置 /about-us
重新定向到 /about
,首先監聽 '/about-us'
,重新定向的部分使用 res.redirect()
,參數為要導向的 URL
1 | app.get('/about-us', (req, res) => { |
全部的 code
1 | const express = require('express'); |
執行,在網址欄輸入 localhost:3000/about-us
結果自然導向 /about
頁面,狀態為 301
使用 res.redirect()
比起在 server.js 的方式要簡單的多,且會自動判斷狀態代碼。
最後將找不到的頁面導向 404.html 的部分使用 app.use()
稱為中介軟體,使用起來像前面的 app.get()
,但可以不添加路徑(第一個參數),結果會每當收到請求時,就會執行此函數,也就是不限於特定的 URL 都會執行
1 | app.use((req, res) => { |
這裡你可能會問每次收到請求都會執行,那麼不論如何瀏覽器不都會呈現 404.html 的頁面嗎?
說明
以上函數會對每個傳入的請求觸發,但前提是請求必須到達 code 的這一段,就像 server.js 中的 switch
一樣,當瀏覽器發出請求時,我們會先透過 switch
判斷 req.url
為何,自上而下的比對 case 選項,一但遇到符合的便會執行然後 break 跳出,而 express 也一樣遇到匹配的路徑就不再執行剩餘的 code,其它功能也就不會觸發。
所以簡單來說將以上的 code 放在最後的部分,當前面的路徑都不匹配最後自然就會執行最後的這一段,與 switch
的 default
有異曲同工之妙。
不過要注意使用這種方式要手動設置 status code,否則會顯示 200,這裡可以使用鏈式寫法的方式撰寫,因為 res.status()
會回傳物件本身,當然要分開寫也是 ok 的。
1 | app.use((req, res) => { |
全部的 code
1 | const express = require('express'); |
執行,在網址輸入 localhost:3000/123
,顯示 404.html
且狀態為 404
這裡我們再試著將 app.use()
往上提,放在 '/'
後面
1 | const express = require('express'); |
執行,在網址輸入 localhost:3000
,正常顯示 index.html 頁面
但輸入 localhost:3000/about
或 localhost:3000/about-us
都會顯示 404 頁面,狀態也是 404
就如前面所說。
不過 app.use()
也可以添加裝載路徑的第一個參數,就像 app.get()
一樣,但不同的是會對該路徑上任何類型的 HTTP 要求執行此函數,前面出現的 get 就是 HTTP 要求的一種類型,其它還有 put、post … 等等類型,這些可以參考 express 的官方文件有更多的說明示範。
學到這裡可以與之前的 server.js 比較,發現用到的 code 更簡短、可讀性較高、更容易維護,所以之後就不用 server.js 的方式在後續的學習上了。
參考資料
The Net Ninja | Node.js Crash Course Tutorial #6 - Express Apps