迁移到 Express 4

¥Moving to Express 4

概述

Express 4 是 Express 3 的重大变化。这意味着如果你在其依赖中更新 Express 版本,现有的 Express 3 应用将无法运行。

¥Express 4 is a breaking change from Express 3. That means an existing Express 3 app will not work if you update the Express version in its dependencies.

本文涵盖:

¥This article covers:

Express 4 的变化

Express 4 有几个重大变化:

¥There are several significant changes in Express 4:

也可以看看:

¥See also:

对 Express 核心和中间件系统的更改

Express 4 不再依赖 Connect,并从其核心中删除了所有内置中间件,除了 express.static 功能。这意味着 Express 现在是一个独立的路由和中间件 Web 框架,并且 Express 版本控制和发布不受中间件更新的影响。

¥Express 4 no longer depends on Connect, and removes all built-in middleware from its core, except for the express.static function. This means that Express is now an independent routing and middleware web framework, and Express versioning and releases are not affected by middleware updates.

如果没有内置中间件,你必须显式添加运行应用所需的所有中间件。只需按照以下步骤操作:

¥Without built-in middleware, you must explicitly add all the middleware that is required to run your app. Simply follow these steps:

  1. 安装模块:npm install --save <module-name>

    ¥Install the module: npm install --save <module-name>

  2. 在你的应用中,需要模块:require('module-name')

    ¥In your app, require the module: require('module-name')

  3. 根据其文档使用该模块:app.use( ... )

    ¥Use the module according to its documentation: app.use( ... )

下表列出了 Express 3 中间件及其在 Express 4 中的对应项。

¥The following table lists Express 3 middleware and their counterparts in 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 中间件的 完整列表

¥Here is the complete list of Express 4 middleware.

在大多数情况下,你可以简单地将旧版本 3 中间件替换为 Express 4 对应的中间件。有关详细信息,请参阅 GitHub 中的模块文档。

¥In most cases, you can simply replace the old version 3 middleware with its Express 4 counterpart. For details, see the module documentation in GitHub.

app.use accepts parameters

在版本 4 中,你可以使用可变参数来定义加载中间件函数的路径,然后从路由处理程序中读取参数的值。例如:

¥In version 4 you can use a variable parameter to define the path where middleware functions are loaded, then read the value of the parameter from the route handler. For example:

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

路由系统

应用现在隐式加载路由中间件,因此你不再需要担心中间件相对于 router 中间件的加载顺序。

¥Apps now implicitly load routing middleware, so you no longer have to worry about the order in which middleware is loaded with respect to the router middleware.

你定义路由的方式没有改变,但路由系统有两个新功能可以帮助你组织路由:

¥The way you define routes is unchanged, but the routing system has two new features to help organize your routes:

app.route() method

新的 app.route() 方法使你能够为路由路径创建可链式的路由处理程序。因为路径是在单个位置指定的,所以创建模块化路由是有帮助的,因为这有助于减少冗余和拼写错误。有关路由的更多信息,请参阅 Router() 文档

¥The new app.route() method enables you to create chainable route handlers for a route path. Because the path is specified in a single location, creating modular routes is helpful, as is reducing redundancy and typos. For more information about routes, see Router() documentation.

这是使用 app.route() 函数定义的链式路由处理程序的示例。

¥Here is an example of chained route handlers that are defined by using the app.route() function.

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 class

另一个有助于组织路由的特性是一个新类 express.Router,你可以使用它来创建模块化的可挂载路由处理程序。一个 Router 实例就是一个完整的中间件和路由系统;因此,它通常被称为 “mini-app”。

¥The other feature that helps to organize routes is a new class, express.Router, that you can use to create modular mountable route handlers. A Router instance is a complete middleware and routing system; for this reason it is often referred to as a “mini-app”.

以下示例将路由创建为模块,在其中加载中间件,定义一些路由,并将其安装在主应用的路径上。

¥The following example creates a router as a module, loads middleware in it, defines some routes, and mounts it on a path on the main app.

例如,在 app 目录下创建一个名为 birds.js 的路由文件,内容如下:

¥For example, create a router file named birds.js in the app directory, with the following content:

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

然后,在应用中加载路由模块:

¥Then, load the router module in the app:

var birds = require('./birds')

// ...

app.use('/birds', birds)

该应用现在将能够处理对 /birds/birds/about 路径的请求,并将调用特定于该路由的 timeLog 中间件。

¥The app will now be able to handle requests to the /birds and /birds/about paths, and will call the timeLog middleware that is specific to the route.

其他变化

下表列出了 Express 4 中其他小的但重要的变化:

¥The following table lists other small but important changes in 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

¥Here is an example of migrating an Express 3 application to Express 4. The files of interest are app.js and package.json.

版本 3 应用

app.js

考虑具有以下 app.js 文件的 Express v.3 应用:

¥Consider an Express v.3 application with the following app.js file:

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 文件可能如下所示:

¥The accompanying version 3 package.json file might look something like this:

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

进程

通过安装 Express 4 应用所需的中间件并使用以下命令将 Express 和 Pug 更新到各自的最新版本来开始迁移过程:

¥Begin the migration process by installing the required middleware for the Express 4 app and updating Express and Pug to their respective latest version with the following command:

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

app.js 进行以下更改:

¥Make the following changes to app.js:

  1. 内置的 Express 中间件函数 express.faviconexpress.loggerexpress.methodOverrideexpress.sessionexpress.bodyParserexpress.errorHandlerexpress 对象上不再可用。你必须手动安装它们的替代品并将它们加载到应用中。

    ¥The built-in Express middleware functions express.favicon, express.logger, express.methodOverride, express.session, express.bodyParser and express.errorHandler are no longer available on the express object. You must install their alternatives manually and load them in the app.

  2. 你不再需要加载 app.router 函数。它不是有效的 Express 4 应用对象,因此请删除 app.use(app.router); 代码。

    ¥You no longer need to load the app.router function. It is not a valid Express 4 app object, so remove the app.use(app.router); code.

  3. 确保中间件函数以正确的顺序加载 - 加载应用路由后加载 errorHandler

    ¥Make sure that the middleware functions are loaded in the correct order - load errorHandler after loading the app routes.

版本 4 应用

package.json

运行上述 npm 命令将更新 package.json,如下所示:

¥Running the above npm command will update package.json as follows:

{
  "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 文件将如下所示:

¥Then, remove invalid code, load the required middleware, and make other changes as necessary. The app.js file will look like this:

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),否则不需要加载它,并且可以通过以下方式简单启动应用:

¥Unless you need to work directly with the http module (socket.io/SPDY/HTTPS), loading it is not required, and the app can be simply started this way:

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

运行应用

迁移过程已完成,该应用现在是 Express 4 应用。要确认,请使用以下命令启动应用:

¥The migration process is complete, and the app is now an Express 4 app. To confirm, start the app by using the following command:

$ node .

加载 http://localhost:3000 并查看 Express 4 正在渲染的主页。

¥Load http://localhost:3000 and see the home page being rendered by Express 4.

升级到 Express 4 应用生成器

生成 Express 应用的命令行工具依然是 express,但要升级到新版本,必须卸载 Express 3 应用生成器,然后安装新的 express-generator

¥The command-line tool to generate an Express app is still express, but to upgrade to the new version, you must uninstall the Express 3 app generator and then install the new express-generator.

安装

如果你的系统上已经安装了 Express 3 应用生成器,则必须将其卸载:

¥If you already have the Express 3 app generator installed on your system, you must uninstall it:

$ npm uninstall -g express

根据你的文件和目录权限的配置方式,你可能需要使用 sudo 运行此命令。

¥Depending on how your file and directory privileges are configured, you might need to run this command with sudo.

现在安装新的生成器:

¥Now install the new generator:

$ npm install -g express-generator

根据你的文件和目录权限的配置方式,你可能需要使用 sudo 运行此命令。

¥Depending on how your file and directory privileges are configured, you might need to run this command with sudo.

现在你系统上的 express 命令已更新为 Express 4 生成器。

¥Now the express command on your system is updated to the Express 4 generator.

应用生成器的更改

命令选项和使用基本保持不变,但有以下例外:

¥Command options and use largely remain the same, with the following exceptions:

示例

执行以下命令创建 Express 4 应用:

¥Execute the following command to create an Express 4 app:

$ express app4

如果查看 app4/app.js 文件的内容,你会注意到应用所需的所有中间件函数(express.static 除外)都作为独立模块加载,并且 router 中间件不再显式加载到应用中。

¥If you look at the contents of the app4/app.js file, you will notice that all the middleware functions (except express.static) that are required for the app are loaded as independent modules, and the router middleware is no longer explicitly loaded in the app.

你还会注意到 app.js 文件现在是一个 Node.js 模块,与旧生成器生成的独立应用不同。

¥You will also notice that the app.js file is now a Node.js module, in contrast to the standalone app that was generated by the old generator.

安装依赖后,使用以下命令启动应用:

¥After installing the dependencies, start the app by using the following command:

$ npm start

如果你查看 package.json 文件中的 npm start 脚本,你会注意到启动应用的实际命令是 node ./bin/www,在 Express 3 中它曾经是 node app.js

¥If you look at the npm start script in the package.json file, you will notice that the actual command that starts the app is node ./bin/www, which used to be node app.js in Express 3.

因为 Express 4 生成器生成的 app.js 文件现在是一个 Node.js 模块,所以它不能再作为应用独立启动(除非你修改代码)。该模块必须加载到 Node.js 文件中并通过 Node.js 文件启动。在这种情况下,Node.js 文件是 ./bin/www

¥Because the app.js file that was generated by the Express 4 generator is now a Node.js module, it can no longer be started independently as an app (unless you modify the code). The module must be loaded in a Node.js file and started via the Node.js file. The Node.js file is ./bin/www in this case.

bin 目录和无扩展名的 www 文件都不是创建 Express 应用或启动应用所必需的。它们只是生成器提出的建议,因此请随意修改它们以满足你的需求。

¥Neither the bin directory nor the extensionless www file is mandatory for creating an Express app or starting the app. They are just suggestions made by the generator, so feel free to modify them to suit your needs.

要删除 www 目录并保留 “Express 3 的方式” 目录,请删除 app.js 文件末尾的 module.exports = app; 行,然后将以下代码粘贴到其位置:

¥To get rid of the www directory and keep things the “Express 3 way”, delete the line that says module.exports = app; at the end of the app.js file, then paste the following code in its place:

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 模块:

¥Ensure that you load the debug module at the top of the app.js file by using the following code:

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

接下来,将 package.json 文件中的 "start": "node ./bin/www" 更改为 "start": "node app.js"

¥Next, change "start": "node ./bin/www" in the package.json file to "start": "node app.js".

你现在已将 ./bin/www 的功能移回 app.js。不建议进行此更改,但该练习可帮助你了解 ./bin/www 文件的工作原理以及 app.js 文件不再自行启动的原因。

¥You have now moved the functionality of ./bin/www back to app.js. This change is not recommended, but the exercise helps you to understand how the ./bin/www file works, and why the app.js file no longer starts on its own.