Node.js — 從一個實例看Express 的運作方式. 接續前兩篇的介紹,這一篇我們要再進一步的討論關於Node.js與Express的… | by Sean Yeh | Web Design Zone | Medium

Node.js — 從一個實例看Express 的運作方式

Photo by Ethan McArthur on Unsplash

接續前兩篇的介紹,這一篇我們要再進一步的討論關於Node.js與Express的使用。進行方式將從實作一個例子來暸解它,這個例子與前一篇有點類似。下面是這個實作的大致流程圖。

可以看到,這是一個只有三個頁面的簡單互動。然而,透過這個簡單的互動,我們可以更深入的了解Express。

環境設定

首先我們一樣要進行環境設定。

在Node.js安裝的狀態下。

1. 初始化:

$ npm init

2.安裝express

$ npm install express --save

完成後的package.json如下,目前的dependencies中只有express:

{

"name": "express_exone",

"version": "1.0.0",

"description": "",

"main": "index.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1"

},

"author": "",

"license": "MIT",

"dependencies": {

"express": "^4.17.1"

}

}

載入express

接下來要載入express。開啟一個檔案,並且命名為index.js。依照下面順序撰寫程式碼:

  1. 載入express模組。指定給變數express。(let express = require(‘express’);)
  2. 使用express。指定給變數app。(let app = express();)
  3. 設定port位置。指定3000 port給變數port(let port = 3000;)
  4. 監聽 port ( app.listen(port); )
  5. 首頁反應:只要到了首頁(http://localhost:3000),伺服器就會傳遞Hello給瀏覽器。

app.get('/', function (req, res) {

res.send('Hello')

})

完整程式碼如下:

let express = require('express');

//1.載入express模組

let app = express();

// 2.使用express

app.get('/', function (req, res) {

res.send('Hello')

})

// 5.首頁

let port = 3000;

//3.設定port位置

app.listen(port);

// 4.監聽 port

如此,就完成了一個基本的server。

使用Express建立伺服器時

當我們比較一下建立伺服器的方式,可以發現使用Express比單純的使用Node撰寫伺服器方便多了。除此之外,是否大家也發現,想要看剛剛修改的變化時,就必須先結束目前執行中的伺服器,並且再重新開啟一次伺服器。即使只是修改了一點點程式碼,都需要經過這流程。這個流程表面上,似乎佔不到多少時間。然而長期下來,當程式碼越來越複雜的時,就不是很好的方式了。這個時候,我們希望有一個程式,可以幫助我們在儲存已修改的程式時,自動的替我重新啟動伺服器。

沒錯,的確有這種套件,名字叫做 nodemon (node monitor)。我們可以透過NPM將nodemon安裝在我們的專案中。

$ npm install nodemon --save-dev

安裝完nodemon後,我們可以在command line 執行

$ nodemon index.js

之後,就不需要不斷的進行關閉重啟主機的步驟了。

輸入表單

接下來,我們要依照上面的流程圖來製作頁面。首先,我們需要一個表單讓使用者輸入答案。因此,我們將首頁改造一下。

app.get('/', function (req, res) {

res.send('Hello')

})

首頁路由中,我們要改造 res.send(‘Hello’)這個地方。原本只傳送Hello字串到瀏覽器,現在我們要改成傳遞HTML表單。HTML表單裡面有action、method屬性,也有input與button標籤。autocomplete 設定為 “off”,以避免瀏覽器自動填入資料。 這些都是與基本的HTML form一樣。

app.get('/', function (req, res) {

res.send(`

<form action="/answer" method="POST">

<p>猜猜看,我喜歡什麼顏色的衣服?</p>

<input name="preferColor" autocomplete="off">

<button>送出</button>

</form>`)

})

如果現在啟動伺服器的話,你可以看到下面畫面:

啟動後首頁畫面

你可以觀察一下,如果按下「送出」鈕會出現什麼變化。

這時候你應該會看到下面的畫面:網址走到answer頁。但是顯示無法POST到answer。

顯示錯誤訊息

由於,當我們使用網址列輸入路徑的時候,我們對伺服器走的是GET request,反之當我們按下「送出」鈕時,走的卻是POST request。GET與POST的不同處,我們之前已經談過了。這裡,由於用的是 POST request,因此我們要寫:

app.post('/answer', function (req, res) {

res.send("感謝送出表單")

})

注意,我們這裡使用app.post()而不是app.get()。如此,當我們再次按下「送出」鈕後,會出現下面的畫面,網址列也會走到/answer的地方:

判斷表單輸入的內容

接下來,我們可以來調整app.post中間的程式碼。根據流程圖上面的規劃,我們希望使用者在答對時(輸入的答案是:紅色),顯示一個「答對了,…」的畫面,而答錯時則顯示另外一個「對不起,…」的畫面。這時候,可以使用 if~else的邏輯判斷式來處理這個流程。因此,邏輯可以這樣寫:

if (答案X == "紅色") {

res.send("答對了,你真暸解我")

} else {

res.send("對不起,不是這個答案")

}

其中,答案X == “紅色”這個地方,要如何處理?「答案X」是使用者從input 輸入框輸入的資料,我們要如何取得?

我們可以用:

req.body.[input欄位的名稱]

來取得畫面上輸入的資料。由<input name="preferColor" autocomplete="off">可以知道,input欄位的名稱為preferColor。所以,我們可以用 req.body.preferColor 取得。

加上前面的if判斷式,整個邏輯可以寫成:

app.post('/answer', function (req, res) {

if (req.body.preferColor == "紅色") {

res.send("答對了,你真暸解我")

} else {

res.send("對不起,不是這個答案")

}

res.send("感謝送出表單")

})

測試一下。結果出現錯誤。伺服器無法讀取preferColor裡面的值。

無法讀取的原因是我們少加上一個Middleware。

Middleware中介軟體

什麼是Middleware?Middleware中文翻譯為「中介軟體」,是指從發出請求後,到接收回應前,用來處理特定用途的程式。簡單來說是一個可以接收request 和 response 物件的函數,每個中介軟體可以針對所收到的物件進行修改處理或是解析處理,處理完畢後再決定是否繼續傳給下個中介軟體或是中斷傳遞行為。

需要Middleware

下面的程式碼,必須放在路由的前面,建議放在 let app = express(); 的後方。使用 app.use()的方式執行Middleware。

app.use(express.urlencoded({

extended: false

}))

再測試一下。這次應該成功了。

本實例的意義

在上面這個簡單的案例中,我們可以發現下面幾件事:

HTTP Request的生命週期

在上面實際的案例中,我們可以知道伺服器接受Request並且依照Request提供對應的 Response,這個Request 與 Response的溝通行為,是HTTP Request的一個生命週期。

如果我們把這個生命週期比喻為一個漢堡的話,Request 與 Response分別代表漢堡的是漢堡最上面與最下面的兩片麵包。兩片麵包的中間,可以放各式各樣的醬料與食材。而這些醬料與食材,就是我們希望伺服器執行的內容。

Photo by Pablo Merchán Montes on Unsplash

存在Request 與 Response 之間程式碼

前面說到,Request 與 Response 之間程式,就像是一個漢堡兩片麵包的中間的醬料與食材。在這個實例中,麵包的中間的醬料是那段由 if~else邏輯判斷式帶出的各種行動。諸如儲存與取出資料庫雌料、載入樣板、登入登出、安全認證、錯誤提示等等。這些都可以說是中間的醬料與食材。

儲存資料

在這個例子中,「使用者輸入字串後,程式就針對該字串進行判斷,並且提供相對應的頁面。」在這個過程中,使用者所輸入的字串並沒有被伺服器記憶下來。對於簡單的網站來說,或許這樣子就足夠了。但是我們可以放眼實際上網路中的各種應用服務,大多需要把使用者輸入的訊息記憶下來。例如購物網站要求使用者輸入帳號密碼來註冊會員,這時候,就需要把帳號密碼等資訊儲存在一個地方,提供未來該使用者重新登入時可以核對身份使用。如要將這些使用者輸入的訊息與資料記憶下來,就還需要一個要素,那就是資料庫。之後我們會繼續說明程式如何存取資料庫。