黑人生命至上。
支持平等正義倡議.

移轉至 Express 4

概觀

Express 4 是 Express 3 的重大變更。這表示現有的 Express 3 應用程式在更新其相依項目的 Express 版本後,將無法正常運作。

本文涵蓋

Express 4 的變更

Express 4 有幾個重大變更

另請參閱

Express 核心和中介軟體系統的變更

Express 4 不再依賴 Connect,並從其核心移除所有內建中介軟體,僅保留 express.static 函式。這表示 Express 現在是一個獨立的路由和中介軟體網路架構,而 Express 的版本和發行不受中介軟體更新影響。

在沒有內建中介軟體的情況下,您必須明確新增執行應用程式所需的所有中介軟體。只需遵循下列步驟

  1. 安裝模組:npm install --save <module-name>
  2. 在您的應用程式中,需要模組:require('module-name')
  3. 根據其文件使用模組:app.use( ... )

下表列出 Express 3 中介軟體及其在 Express 4 中的對應中介軟體。

Express 3Express 4
express.bodyParser body-parser + multer
express.compress compression
express.cookieSession cookie-session
express.cookieParser cookie-parser
express.logger morgan
express.session express-session
express.favicon serve-favicon
express.responseTime response-time
express.errorHandler errorhandler
express.methodOverride method-override
express.timeout connect-timeout
express.vhost vhost
express.csrf csurf
express.directory serve-index
express.static serve-static

以下是 Express 4 中介軟體的完整清單

在多數情況下,你可以簡單地用 Express 4 對應的版本取代舊的版本 3 中介軟體。詳細資訊請參閱 GitHub 中的模組文件。

app.use 接受參數

在版本 4 中,你可以使用變數參數來定義載入中介軟體函式的路徑,然後從路由處理程式中讀取參數的值。例如

app.use('/book/:id', (req, res, next) => {
  console.log('ID:', req.params.id)
  next()
})

路由系統

應用程式現在會隱式載入路由中介軟體,因此你不再需要擔心中介軟體載入的順序是否與 router 中介軟體有關。

你定義路由的方式沒有改變,但路由系統有兩個新功能,可以協助你整理路由

app.route() 方法

新的 app.route() 方法讓你能夠為路由路徑建立可串接的路由處理程式。由於路徑是在單一位置指定的,因此建立模組化路由非常有幫助,而且可以減少重複和錯字。如需有關路由的更多資訊,請參閱 Router() 文件

以下是使用 app.route() 函式定義的串接路由處理程式的範例。

app.route('/book')
  .get((req, res) => {
    res.send('Get a random book')
  })
  .post((req, res) => {
    res.send('Add a book')
  })
  .put((req, res) => {
    res.send('Update the book')
  })

express.Router 類別

另一個有助於整理路由的功能是一個新的類別,express.Router,你可以使用它來建立可模組化、可掛載的路由處理程式。Router 執行個體是一個完整的軟體和路由系統;因此,它通常稱為「迷你應用程式」。

以下範例建立一個路由器作為模組,載入其中的中介軟體,定義一些路由,並將它掛載在主應用程式的路徑上。

例如,在應用程式目錄中建立一個名為 birds.js 的路由器檔案,內容如下

var express = require('express')
var router = express.Router()

// middleware specific to this router
router.use((req, res, next) => {
  console.log('Time: ', Date.now())
  next()
})
// define the home page route
router.get('/', (req, res) => {
  res.send('Birds home page')
})
// define the about route
router.get('/about', (req, res) => {
  res.send('About birds')
})

module.exports = router

然後,在應用程式中載入路由器模組

var birds = require('./birds')

// ...

app.use('/birds', birds)

應用程式現在可以處理對 /birds/birds/about 路徑的請求,並且會呼叫特定於路由的 timeLog 中介軟體。

其他變更

下表列出 Express 4 中其他一些小但重要的變更

物件 說明
Node.js Express 4 需要 Node.js 0.10.x 或更新版本,且已停止支援 Node.js 0.8.x。

http.createServer()

不再需要 http 模組,除非您需要直接使用它 (socket.io/SPDY/HTTPS)。可以使用 app.listen() 函式啟動應用程式。

app.configure()

已移除 app.configure() 函式。使用 process.env.NODE_ENVapp.get('env') 函式來偵測環境並相應地設定應用程式。

json spaces

在 Express 4 中,json spaces 應用程式屬性預設已停用。

req.accepted()

使用 req.accepts()req.acceptsEncodings()req.acceptsCharsets()req.acceptsLanguages()

res.location()

不再解析相對 URL。

req.params

以前是陣列;現在是物件。

res.locals

以前是函式;現在是物件。

res.headerSent

已變更為 res.headersSent

app.route

現在可用為 app.mountpath

res.on('header')

已移除。

res.charset

已移除。

res.setHeader('Set-Cookie', val)

功能現在僅限於設定基本 cookie 值。對於新增功能,請使用 res.cookie()

範例應用程式遷移

以下是將 Express 3 應用程式遷移至 Express 4 的範例。相關檔案為 app.jspackage.json

版本 3 應用程式

app.js

考慮一個 Express v.3 應用程式,其具有以下 app.js 檔案

var express = require('express')
var routes = require('./routes')
var user = require('./routes/user')
var http = require('http')
var path = require('path')

var app = express()

// all environments
app.set('port', process.env.PORT || 3000)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')
app.use(express.favicon())
app.use(express.logger('dev'))
app.use(express.methodOverride())
app.use(express.session({ secret: 'your secret here' }))
app.use(express.bodyParser())
app.use(app.router)
app.use(express.static(path.join(__dirname, 'public')))

// development only
if (app.get('env') === 'development') {
  app.use(express.errorHandler())
}

app.get('/', routes.index)
app.get('/users', user.list)

http.createServer(app).listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'))
})

package.json

隨附的版本 3 package.json 檔案可能類似以下內容

{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.12.0",
    "pug": "*"
  }
}

處理

透過安裝 Express 4 應用程式所需的中間件,並將 Express 和 Pug 更新至其各自的最新版本,以以下指令開始遷移處理

$ npm install serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latest --save

app.js 進行以下變更

  1. 內建 Express 中間件函式 express.faviconexpress.loggerexpress.methodOverrideexpress.sessionexpress.bodyParserexpress.errorHandler 不再於 express 物件中提供。您必須手動安裝其替代方案,並將其載入應用程式中。

  2. 您不再需要載入 app.router 函式。它不是有效的 Express 4 應用程式物件,因此請移除 app.use(app.router); 程式碼。

  3. 請確定中間件函式已載入正確順序 - 在載入應用程式路由後載入 errorHandler

版本 4 應用程式

package.json

執行上述 npm 指令將會更新 package.json 如下

{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "body-parser": "^1.5.2",
    "errorhandler": "^1.1.1",
    "express": "^4.8.0",
    "express-session": "^1.7.2",
    "pug": "^2.0.0",
    "method-override": "^2.1.2",
    "morgan": "^1.2.2",
    "multer": "^0.1.3",
    "serve-favicon": "^2.0.1"
  }
}

app.js

然後,移除無效程式碼、載入所需的中間件,並視需要進行其他變更。app.js 檔案將會類似以下內容

var http = require('http')
var express = require('express')
var routes = require('./routes')
var user = require('./routes/user')
var path = require('path')

var favicon = require('serve-favicon')
var logger = require('morgan')
var methodOverride = require('method-override')
var session = require('express-session')
var bodyParser = require('body-parser')
var multer = require('multer')
var errorHandler = require('errorhandler')

var app = express()

// all environments
app.set('port', process.env.PORT || 3000)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')
app.use(favicon(path.join(__dirname, '/public/favicon.ico')))
app.use(logger('dev'))
app.use(methodOverride())
app.use(session({
  resave: true,
  saveUninitialized: true,
  secret: 'uwotm8'
}))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(multer())
app.use(express.static(path.join(__dirname, 'public')))

app.get('/', routes.index)
app.get('/users', user.list)

// error handling middleware should be loaded after the loading the routes
if (app.get('env') === 'development') {
  app.use(errorHandler())
}

var server = http.createServer(app)
server.listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'))
})

除非您需要直接使用 http 模組 (socket.io/SPDY/HTTPS),否則不需要載入它,而應用程式可以單純以這種方式啟動

app.listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'))
})

執行應用程式

遷移處理已完成,而應用程式現在是 Express 4 應用程式。為確認,請使用以下指令啟動應用程式

$ node .

載入 https://127.0.0.1:3000 並查看由 Express 4 呈現的首頁。

升級至 Express 4 應用程式產生器

產生 Express 應用程式的命令列工具仍然是 express,但若要升級至新版本,您必須先解除安裝 Express 3 應用程式產生器,然後安裝新的 express-generator

安裝

如果您系統上已安裝 Express 3 應用程式產生器,您必須將其解除安裝

$ npm uninstall -g express

根據您的檔案和目錄權限設定,您可能需要使用 sudo 執行此命令。

現在安裝新的產生器

$ npm install -g express-generator

根據您的檔案和目錄權限設定,您可能需要使用 sudo 執行此命令。

現在您系統上的 express 命令已更新為 Express 4 產生器。

應用程式產生器的變更

命令選項和使用方式大致保持不變,但有下列例外

範例

執行下列命令以建立 Express 4 應用程式

$ express app4

如果您查看 app4/app.js 檔案的內容,您會注意到所有應用程式所需的中間件函式(除了 express.static)都載入為獨立模組,而且 router 中間件不再明確載入應用程式中。

您也會注意到 app.js 檔案現在是 Node.js 模組,與舊產生器產生的獨立應用程式不同。

安裝相依項後,使用下列命令啟動應用程式

$ npm start

如果您查看 package.json 檔案中的 npm start 腳本,您會注意到實際啟動應用程式的命令是 node ./bin/www,在 Express 3 中為 node app.js

由於 Express 4 產生器產生的 app.js 檔案現在是 Node.js 模組,因此它不再能獨立作為應用程式啟動(除非您修改程式碼)。必須在 Node.js 檔案中載入模組,並透過 Node.js 檔案啟動。在本例中,Node.js 檔案是 ./bin/www

bin 目錄或沒有副檔名的 www 檔案對於建立 Express 應用程式或啟動應用程式都不是必要的。它們只是產生器提出的建議,因此您可以根據自己的需要修改它們。

若要移除 www 目錄並維持「Express 3 方式」,請刪除 app.js 檔案結尾的 module.exports = app; 行,然後貼上以下程式碼取代

app.set('port', process.env.PORT || 3000)

var server = app.listen(app.get('port'), () => {
  debug('Express server listening on port ' + server.address().port)
})

請務必使用以下程式碼在 app.js 檔案的開頭載入 debug 模組

var debug = require('debug')('app4')

接著,將 package.json 檔案中的 "start": "node ./bin/www" 變更為 "start": "node app.js"

您現在已將 ./bin/www 的功能移回 app.js。不建議進行此變更,但此練習有助於您了解 ./bin/www 檔案如何運作,以及 app.js 檔案為何不再自行啟動。