翻译此页面

迁移至 Express 4

Express 4 相对于 Express 3 是一个破坏性变更版本。 这意味着如果在依赖项中更新 Express 版本,现有的 Express 3 应用将无法正常运行。

本文包括:

Express 4 的变更

Express 4 中有几项重大变更:

参阅:

Express 核心和中间件系统的变更

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

由于不再包含内置中间件,你必须显式添加应用运行所需的全部中间件。 只需按照以下步骤操作:

  1. Install the module: 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.compresscompression
express.cookieSessioncookie-session
express.cookieParsercookie-parser
express.loggermorgan
express.sessionexpress-session
express.faviconserve-favicon
express.responseTimeresponse-time
express.errorHandlererrorhandler
express.methodOverridemethod-override
express.timeoutconnect-timeout
express.vhostvhost
express.csrfcsurf
express.directoryserve-index
express.staticserve-static

此处为 Express 4 中间件的完整清单

大多数情况下,你只需将旧版3中间件替换为对应的Express 4版本即可。 详情请参阅 GitHub 上的模块文档。

app.use 接受参数

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

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

路由系统

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

定义路由的方式未发生改变,但路由系统新增了两项功能,可帮助你更好地组织路由:

  • 新增方法 app.route(),用于为路由路径创建可链式调用的路由处理程序。
  • 新增类 express.Router,用于创建模块化的、可挂载的路由处理程序。

app.route() 方法

新增的 app.route() 方法支持你为路由路径创建可链式调用的路由处理程序。 由于路径在单一位置指定,因此创建模块化路由十分实用,同时还能减少冗余代码和拼写错误。 For more information about routes, see Router() documentation.

以下是使用 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 的路由文件,内容如下:

birds.cjs
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;

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

index.cjs
var birds = require('./birds');
// ...
app.use('/birds', birds);

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

其他改变:

下表列出了 Express 4 中其他细微但重要的变更:

ObjectDescription
Node.jsExpress 4 requires 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 spacesExpress 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 3 版本的应用拥有如下 app.js 文件:

index.cjs
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

对应的 Express 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 更新至各自的最新版本。

Terminal window
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文件看起来像这样:

index.cjs
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'));
});

Note

除非你需要直接操作 http 模块(socket.io/SPDY/HTTPS),否则无需加载该模块,应用可通过以下方式直接启动:

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

运行应用

迁移流程已完成,当前应用已升级为 Express 4 应用。 若要确认,请使用以下命令启动应用:

Terminal window
$ node .

访问 http://localhost:3000,查看由 Express 4 渲染的首页。

升级至 Express 4 应用生成器

用于生成 Express 应用的命令行工具仍为 express,但要升级至新版本,你必须先卸载 Express 3 应用生成器,然后安装全新的 express-generator

安装

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

Terminal window
npm uninstall -g express

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

现在安装新版生成器:

Terminal window
npm install -g express-generator

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

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

更改应用生成器

命令选项和用法基本保持不变,仅存在以下例外:

  • 已移除--sessions选项。
  • 移除了 “—jshtml” 选项。
  • Added the --hogan option to support Hogan.js.

示例

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

Terminal window
$ express app4

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

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

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

Terminal window
$ npm start

查看 package.json 文件中的 npm start 脚本,你会发现启动应用的实际命令是 node ./bin/www,而在 Express 3 中该命令为 node app.js

由于 Express 4 生成器生成的 app.js 文件现已成为一个 Node.js 模块,因此无法再作为应用独立启动(除非你修改代码)。 必须将该模块加载到 Node.js 文件中,并通过该文件启动应用。 在本例中,这个 Node.js 文件就是 ./bin/www

创建或启动 Express 应用时,bin 目录和无后缀的 www 文件都不是必需的。 这些仅为生成器给出的建议,你可按需自行修改。

若要移除 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 无法独立启动的原因。