首頁 > 軟體

NodeJs Express中介軟體超詳細講解

2022-08-24 18:02:32

什麼是中介軟體

中介軟體(Middleware ),特指業務流程的中間處理環節

現實生活中的例子

在處理汙水的時候,一般都要經過三個處理環節,從而保證處理過後的廢水,達到排放標準

處理汙水的這三個中間處理環節,就可以叫做中介軟體。

Express 中介軟體的呼叫流程

當一個請求到達 Express 的伺服器之後,可以連續呼叫多箇中介軟體,從而對這次請求進行預處理。

Express 中介軟體的格式

Express 的中介軟體,本質上就是一個 function 處理常式,Express 中介軟體的格式如下:

注意:中介軟體函數的形參列表中,必須包含 next 引數。而路由處理常式中只包含 req 和 res。

next 函數的作用

next 函數是實現多箇中介軟體連續呼叫的關鍵,它表示把流轉關係轉交給下一個中介軟體或路由。

定義中介軟體函數

可以通過如下的方式,定義一個最簡單的中介軟體函數:

全域性生效的中介軟體

使用者端發起的任何請求,到達伺服器之後,都會觸發的中介軟體,叫做全域性生效的中介軟體。

通過呼叫 app.use(中介軟體函數),即可定義一個全域性生效的中介軟體,範例程式碼如下:

定義全域性中介軟體的簡化形式

const express = require('express')
const app = express()
// // 定義一個最簡單的中介軟體函數
// const mw = function (req, res, next) {
//   console.log('這是最簡單的中介軟體函數')
//   // 把流轉關係,轉交給下一個中介軟體或路由
//   next()
// }
// // 將 mw 註冊為全域性生效的中介軟體
// app.use(mw)
// 這是定義全域性中介軟體的簡化形式
app.use((req, res, next) => {
  console.log('這是最簡單的中介軟體函數')
  next()
})
app.get('/', (req, res) => {
  console.log('呼叫了 / 這個路由')
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  console.log('呼叫了 /user 這個路由')
  res.send('User page.')
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

當在apifox請求這兩個請求時,都會先呼叫這個中介軟體再返回請求的內容。

中介軟體的作用

多箇中介軟體之間,共用同一份 req 和 res。基於這樣的特性,我們可以在上游的中介軟體中,統一為 req 或 res 物件新增自定義的屬性或方法,供下游的中介軟體或路由進行使用。

const express = require('express')
const app = express()
// 這是定義全域性中介軟體的簡化形式
app.use((req, res, next) => {
  // 獲取到請求到達伺服器的時間
  const time = Date.now()
  // 為 req 物件,掛載自定義屬性,從而把時間共用給後面的所有路由
  req.startTime = time
  next()
})
app.get('/', (req, res) => {
  res.send('Home page.' + req.startTime)
})
app.get('/user', (req, res) => {
  res.send('User page.' + req.startTime)
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

定義多個全域性中介軟體

可以使用 app.use() 連續定義多個全域性中介軟體。使用者端請求到達伺服器之後,會按照中介軟體定義的先後順序依次進行呼叫,範例程式碼如下:

const express = require('express')
const app = express()
// 定義第一個全域性中介軟體
app.use((req, res, next) => {
  console.log('呼叫了第1個全域性中介軟體')
  next()
})
// 定義第二個全域性中介軟體
app.use((req, res, next) => {
  console.log('呼叫了第2個全域性中介軟體')
  next()
})
// 定義一個路由
app.get('/user', (req, res) => {
  res.send('User page.')
})
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

區域性生效的中介軟體

不使用 app.use() 定義的中介軟體,叫做區域性生效的中介軟體,範例程式碼如下:

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 1. 定義中介軟體函數
const mw1 = (req, res, next) => {
  console.log('呼叫了區域性生效的中介軟體')
  next()
}
// 2. 建立路由,mw1只在當前路由生效
app.get('/', mw1, (req, res) => {
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  res.send('User page.')
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

定義多個區域性中介軟體

可以在路由中,通過如下兩種等價的方式,使用多個區域性中介軟體:

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 1. 定義中介軟體函數
const mw1 = (req, res, next) => {
  console.log('呼叫了第一個區域性生效的中介軟體')
  next()
}
const mw2 = (req, res, next) => {
  console.log('呼叫了第二個區域性生效的中介軟體')
  next()
}
// 2. 建立路由
app.get('/', [mw1, mw2], (req, res) => {
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  res.send('User page.')
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

瞭解中介軟體的5個使用注意事項

① 一定要在路由之前註冊中介軟體

② 使用者端傳送過來的請求,可以連續呼叫多箇中介軟體進行處理

③ 執行完中介軟體的業務程式碼之後,不要忘記呼叫 next() 函數

④ 為了防止程式碼邏輯混亂,呼叫 next() 函數後不要再寫額外的程式碼

⑤ 連續呼叫多箇中介軟體時,多箇中介軟體之間,共用 req 和 res 物件

中介軟體的分類

為了方便大家理解和記憶中介軟體的使用,Express 官方把常見的中介軟體用法,分成了 5 大類,分別是:

① 應用級別的中介軟體

② 路由級別的中介軟體

③ 錯誤級別的中介軟體

④ Express 內建的中介軟體

⑤ 第三方的中介軟體

應用級別的中介軟體

通過 app.use() 或 app.get() 或 app.post() ,繫結到 app 範例上的中介軟體,叫做應用級別的中介軟體,程式碼範例如下:

路由級別的中介軟體

繫結到 express.Router() 範例上的中介軟體,叫做路由級別的中介軟體。它的用法和應用級別中介軟體沒有任何區別。只不過,應用級別中介軟體是繫結到 app 範例上,路由級別中介軟體系結到 router 範例上,程式碼範例如下:

錯誤級別的中介軟體

錯誤級別中介軟體的作用:專門用來捕獲整個專案中發生的異常錯誤,從而防止專案異常崩潰的問題。

格式:錯誤級別中介軟體的 function 處理常式中,必須有 4 個形參,形參順序從前到後,分別是 (err, req, res, next)。

注意:錯誤級別的中介軟體,必須註冊在所有路由之後!

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 1. 定義路由
app.get('/', (req, res) => {
  // 1.1 人為的製造錯誤
  throw new Error('伺服器內部發生了錯誤!')
  res.send('Home page.')
})
// 2. 定義錯誤級別的中介軟體,捕獲整個專案的異常錯誤,從而防止程式的崩潰
app.use((err, req, res, next) => {
  console.log('發生了錯誤!' + err.message)
  res.send('Error:' + err.message)
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

Express內建的中介軟體

自 Express 4.16.0 版本開始,Express 內建了 3 個常用的中介軟體,極大的提高了 Express 專案的開發效率和體驗:

① express.static 快速託管靜態資源的內建中介軟體,例如: HTML 檔案、圖片、CSS 樣式等(無相容性)

② express.json 解析 JSON 格式的請求體資料(有相容性,僅在 4.16.0+ 版本中可用)

③ express.urlencoded 解析 URL-encoded 格式的請求體資料(有相容性,僅在 4.16.0+ 版本中可用)

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 注意:除了錯誤級別的中介軟體,其他的中介軟體,必須在路由之前進行設定
// 通過 express.json() 這個中介軟體,解析表單中的 JSON 格式的資料
app.use(express.json())
// 通過 express.urlencoded() 這個中介軟體,來解析 表單中的 url-encoded 格式的資料
app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
  // 在伺服器,可以使用 req.body 這個屬性,來接收使用者端傳送過來的請求體資料
  // 預設情況下,如果不設定解析表單資料的中介軟體,則 req.body 預設等於 undefined
  console.log(req.body)
  res.send('ok')
})
app.post('/book', (req, res) => {
  // 在伺服器端,可以通過 req,body 來獲取 JSON 格式的表單資料和 url-encoded 格式的資料
  console.log(req.body)
  res.send('ok')
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

第三方的中介軟體

非 Express 官方內建的,而是由第三方開發出來的中介軟體,叫做第三方中介軟體。在專案中,大家可以按需下載並設定

第三方中介軟體,從而提高專案的開發效率。

例如:在 express@4.16.0 之前的版本中,經常使用 body-parser 這個第三方中介軟體,來解析請求體資料。使用步

驟如下:

① 執行 npm install body-parser 安裝中介軟體

② 使用 require 匯入中介軟體

③ 呼叫 app.use() 註冊並使用中介軟體

注意:Express 內建的 express.urlencoded 中介軟體,就是基於 body-parser 這個第三方中介軟體進一步封裝出來的。

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 1. 匯入解析表單資料的中介軟體 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 註冊中介軟體
app.use(parser.urlencoded({ extended: false }))
// app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
  // 如果沒有設定任何解析表單資料的中介軟體,則 req.body 預設等於 undefined
  console.log(req.body)
  res.send('ok')
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

自定義中介軟體

1. 需求描述與實現步驟

自己手動模擬一個類似於 express.urlencoded 這樣的中介軟體,來解析 POST 提交到伺服器的表單資料。

實現步驟:

① 定義中介軟體

② 監聽 req 的 data 事件

③ 監聽 req 的 end 事件

④ 使用 querystring 模組解析請求體資料

⑤ 將解析出來的資料物件掛載為 req.body

⑥ 將自定義中介軟體封裝為模組

2. 定義中介軟體

使用 app.use() 來定義全域性生效的中介軟體,程式碼如下:

3 .監聽req的data事件

在中介軟體中,需要監聽 req 物件的 data 事件,來獲取使用者端傳送到伺服器的資料。

如果資料量比較大,無法一次性傳送完畢,則使用者端會把資料切割後,分批傳送到伺服器。所以 data 事件可能會觸發多次,每一次觸發 data 事件時,獲取到資料只是完整資料的一部分,需要手動對接收到的資料進行拼接。

4. 監聽req的end事件

當請求體資料接收完畢之後,會自動觸發 req 的 end 事件。

因此,我們可以在 req 的 end 事件中,拿到並處理完整的請求體資料。範例程式碼如下:

5. 使用Qs模組解析請求體資料

Node.js 內建了一個Qs 模組,專門用來處理查詢字串。通過這個模組提供的 parse() 函數,可以輕鬆把查詢字串,解析成物件的格式。範例程式碼如下:

6. 將解析出來的資料物件掛載為req.body

上游的中介軟體和下游的中介軟體及路由之間,共用同一份 req 和 res。因此,我們可以將解析出來的資料,掛載為 req 的自定義屬性,命名為 req.body,供下游使用。範例程式碼如下:

7. 將自定義中介軟體封裝為模組

為了優化程式碼的結構,我們可以把自定義的中介軟體函數,封裝為獨立的模組,範例程式碼如下:

// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 匯入 Node.js 內建的 Qs 模組
const qs = require('Qs')
// 這是解析表單資料的中介軟體
app.use((req, res, next) => {
  // 定義中介軟體具體的業務邏輯
  // 1. 定義一個 str 字串,專門用來儲存使用者端傳送過來的請求體資料
  let str = ''
  // 2. 監聽 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 監聽 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的請求體資料
    // console.log(str)
    // TODO: 把字串格式的請求體資料,解析成物件格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
})
app.post('/user', (req, res) => {
  res.send(req.body)
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

封裝自定義中介軟體

// 匯入 Node.js 內建的 Qs 模組
const qs = require('Qs')
const bodyParser = (req, res, next) => {
  // 定義中介軟體具體的業務邏輯
  // 1. 定義一個 str 字串,專門用來儲存使用者端傳送過來的請求體資料
  let str = ''
  // 2. 監聽 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 監聽 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的請求體資料
    // console.log(str)
    // TODO: 把字串格式的請求體資料,解析成物件格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
}
module.exports = bodyParser
// 匯入 express 模組
const express = require('express')
// 建立 express 的伺服器範例
const app = express()
// 1. 匯入自己封裝的中介軟體模組
const customBodyParser = require('./14.custom-body-parser')
// 2. 將自定義的中介軟體函數,註冊為全域性可用的中介軟體
app.use(customBodyParser)
app.post('/user', (req, res) => {
  res.send(req.body)
})
// 呼叫 app.listen 方法,指定埠號並啟動web伺服器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

到此這篇關於NodeJs Express中介軟體超詳細講解的文章就介紹到這了,更多相關NodeJs Express中介軟體內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com