黑人生命也是命。
支持平權正義倡議.

撰寫用於 Express 應用程式的中間件

概觀

中間件函式是可以存取 請求物件 (req)、回應物件 (res) 以及應用程式請求-回應週期中 next 函式的函式。next 函式是 Express 路由器中的函式,當呼叫時,會執行在目前中間件之後的中間件。

中間件函式可以執行下列任務

如果目前的 middleware 函式並未結束請求-回應循環,它必須呼叫 next() 以將控制權傳遞給下一個 middleware 函式。否則,請求將會懸而未決。

下圖顯示 middleware 函式呼叫的元素

middleware 函式適用的 HTTP 方法。
middleware 函式適用的路徑 (路由)。
middleware 函式。
middleware 函式的回呼引數,依慣例稱為「next」。
middleware 函式的 HTTP 回應 引數,依慣例稱為「res」。
middleware 函式的 HTTP 請求 引數,依慣例稱為「req」。

從 Express 5 開始,傳回 Promise 的 middleware 函式會在拒絕或擲出錯誤時呼叫 next(value)next 會以拒絕的值或擲出的 Error 呼叫。

範例

以下是簡單的「Hello World」Express 應用程式的範例。本文的其餘部分將定義並新增三個 middleware 函式到應用程式:一個稱為 myLogger,用於列印簡單的記錄訊息;一個稱為 requestTime,用於顯示 HTTP 請求的時間戳記;一個稱為 validateCookies,用於驗證輸入的 Cookie。

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

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(3000)

Middleware 函式 myLogger

以下是稱為「myLogger」的 middleware 函式的簡單範例。當請求傳遞至應用程式時,此函式只會列印「LOGGED」。middleware 函式會指定給名為 myLogger 的變數。

const myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

請注意上面呼叫 next() 的部分。呼叫這個函式會呼叫應用程式中的下一個中間件函式。next() 函式並非 Node.js 或 Express API 的一部分,而是傳遞給中間件函式的第三個引數。next() 函式可以命名為任何名稱,但根據慣例,它總是命名為「next」。為避免混淆,請務必遵循此慣例。

若要載入中間件函式,請呼叫 app.use(),並指定中間件函式。例如,以下程式碼會在根路徑(/)的路由之前載入 myLogger 中間件函式。

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

const myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(3000)

每次應用程式收到請求時,它會將訊息「LOGGED」印到終端機。

載入中間件的順序很重要:最先載入的中間件函式也會最先執行。

如果 myLogger 在根路徑的路由之後載入,請求永遠不會到達它,而應用程式也不會印出「LOGGED」,因為根路徑的路由處理常式會終止請求-回應循環。

中間件函式 myLogger 只會印出訊息,然後透過呼叫 next() 函式將請求傳遞給堆疊中的下一個中間件函式。

中間件函式 requestTime

接下來,我們將建立一個稱為「requestTime」的中間件函式,並將一個稱為 requestTime 的屬性新增到請求物件中。

const requestTime = function (req, res, next) {
  req.requestTime = Date.now()
  next()
}

應用程式現在使用 requestTime 中間件函式。此外,根路徑路由的回呼函式使用中間件函式新增到 req(請求物件)的屬性。

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

const requestTime = function (req, res, next) {
  req.requestTime = Date.now()
  next()
}

app.use(requestTime)

app.get('/', (req, res) => {
  let responseText = 'Hello World!<br>'
  responseText += `<small>Requested at: ${req.requestTime}</small>`
  res.send(responseText)
})

app.listen(3000)

當您向應用程式的根目錄發出請求時,應用程式現在會在瀏覽器中顯示您請求的時間戳記。

中間件函式 validateCookies

最後,我們將建立一個中間件函式,用於驗證傳入的 cookie,如果 cookie 無效,則傳送 400 回應。

以下是一個使用外部非同步服務驗證 cookie 的範例函式。

async function cookieValidator (cookies) {
  try {
    await externallyValidateCookie(cookies.testCookie)
  } catch {
    throw new Error('Invalid cookies')
  }
}

在此,我們使用 cookie-parser 中間件來解析 req 物件中的傳入 cookie,並將它們傳遞給我們的 cookieValidator 函式。validateCookies 中間件會傳回一個 Promise,在拒絕時會自動觸發我們的錯誤處理常式。

const express = require('express')
const cookieParser = require('cookie-parser')
const cookieValidator = require('./cookieValidator')

const app = express()

async function validateCookies (req, res, next) {
  await cookieValidator(req.cookies)
  next()
}

app.use(cookieParser())

app.use(validateCookies)

// error handler
app.use((err, req, res, next) => {
  res.status(400).send(err.message)
})

app.listen(3000)

請注意,next() 會在 await cookieValidator(req.cookies) 之後呼叫。這可確保如果 cookieValidator 解決,堆疊中的下一個中間件將會被呼叫。如果您傳遞任何東西給 next() 函式(除了字串 'route''router'),Express 會將目前的請求視為錯誤,並會略過任何剩餘的非錯誤處理路由和中間件函式。

由於您可以存取請求物件、回應物件、堆疊中的下一個中間件函式,以及整個 Node.js API,因此中間件函式的可能性是無窮盡的。

有關 Express 中間件的更多資訊,請參閱:使用 Express 中間件

可設定中間件

如果您需要您的中間件可設定,請匯出一個函式,該函式接受選項物件或其他參數,然後根據輸入參數傳回中間件實作。

檔案:my-middleware.js

module.exports = function (options) {
  return function (req, res, next) {
    // Implement the middleware function based on the options object
    next()
  }
}

現在可以使用中間件,如下所示。

const mw = require('./my-middleware.js')

app.use(mw({ option1: '1', option2: '2' }))

請參閱 cookie-sessioncompression 以取得可設定中間件的範例。