Node.js: Node.js + express - Server & Routing

之前有一篇用Node.js原生語法建立伺服器的文章提過說,一般都會使用express這個模組去建立伺服器,因為用express建立伺服器更簡單、更便利😂

這篇就是記錄如何用express設定伺服器、靜態文件和路由(routing)。


Server

從以下範例可以看出用express設定伺服器確實比使用Node.js原生語法設定伺服器來的簡單許多,但是express的伺服器回應(response)每次都只能 sendsendFile 一次

const path = require('path');
const express = require('express');
const app = express();

/* Request handling */
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, index.html));
    // res.send(<h3>Welcome</h3>);   // 錯誤
});

app.get('/about', (req, res) => {
    res.send('About page');
});


// ... other routing


// 讓伺服器監聽port 3000的請求
app.listen(3000, () => {
    console.log('Server is running on the port 3000');
});



Static files

靜態文件(static files)是任何非伺服器端產生、需要提供給網站的資源,譬如像是CSS、圖片檔等,都可稱作是靜態文件。

在express中,靜態文件的處理是要透過middleware機制去設定放置靜態文件的資料夾,假設放置靜態文件的資料夾名為"public",我們要在路由(routing)之前設定middleware:

// middleware
app.use(express.static('public'));

app.use() 表示要使用middle ware;而 express.static() 括號內則表示要放置靜態文件的資料夾。



Routing & Status Code

當使用者透過連結或者自行輸入網址會導向至相對應的頁面,而路由就是用來達成這樣的事情,這一小節分為三種類型的路由:

  • Routing for patterns
  • Routing for query
  • Routing for all

Routing for patterns

第一個要來處理動態路由,可以透過 req.params 取得參數物件,而範例以解構(destructuring)的方式去取得參數product:

// Routing for patterns
app.get('/products/:product', (req, res) => {
    const { product } = req.params;
    res.send(`The entered product is ${product}.`);
});


Routing for query

第二種路由類型則是要處理表單送出的路由,假設已經在首頁設定好一個表單,表單的action可以設定送出請求後的路由:

<form action="/formHandling" method="GET">
    ...
</form>


以下範例考慮表單的method可設定為GET或POST兩種請求:

method = "GET"

GET請求比較容易,只需要將路由設定成 app.get('/* 表單送出路由 */', ...);

app.get('/formHandling', (req, res) => {
    const { fullname, age } = req.query;   // 取得表單欄位值
    res.send(`Hi, ${fullname}. Your age is ${age}.`)
});


method = "POST"

在express裡面,表單的POST請求則是要多設定一個middleware:

const bodyParser = require('body-parser');

// middleware
app.use(bodyParser.urlencoded({ extended: true }));

// Handle POST request
app.post('/formHandling', (req, res) => {
    const { fullname, age } = req.body;
    res.send(`Hi, ${fullname}. Thanks for posting.`);
});


Routing for all

最後一個要處理的是任一路由,也就是假設使用者輸入沒有特別設定(或者亂打一通)的網址列,可以使用 * 顯示常見的 "404" 畫面:

// Routing for all
app.get('*', (req, res) => {
    res.status(404)   // 設定 404 狀態碼
       .send(<h1> 404 NOT FOUND </h1>);
});

設定這種路由要注意的是,只能放在其他路由的最下面,否則不管輸入什麼網址都會直接轉向至這個路由。



HTTP Requests

常用的HTTP Requests有以下幾種:

  • GET: Read
  • POST: Create
  • PUT: Replace/ Update ALL
  • PATCH: Replace/ Update Partial Data
  • DELETE

在此之前都只用到 GETPOST requests,後端的server可以處理以上提到的五種需求,但是如果前端HTML頁面建立了一個表格提供修改資料,不過HTML Form的method卻只有GET和POST,這種時候可以借助第三方套件來處理其他類型的requests。

這篇文章以method-override示範,根據文件express要新增以下資訊:

const methodOcerride = require("method-override");

// override with POST having ?_method=PUT
app.use(methodOverride("_method"));

然後HTML的form method可以設定成POST,但是action後面要新增參數:

<form action="/update/:id?_method=PUT" method="POST">
...
</form>



References Using middleware @express Serving static files in Express @express 2022網頁開發全攻略(HTML, CSS, JavaScript, React, SQL, Node, more)