🌐 Nodejs.cn

迁移到 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 的变化

🌐 Changes in Express 4

在 Express 4 中有几个重要的变化:

🌐 There are several significant changes in Express 4:

另请参见:

🌐 See also:

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

🌐 Changes to Express core and middleware system

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>
  2. 在你的应用中,引用模块:require('module-name')
  3. 按照文档使用该模块: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.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 中间件的完整列表

🌐 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 接受参数

🌐 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();
});

路由系统

🌐 The routing system

应用现在会隐式加载路由中间件,因此你不再需要担心中间件加载顺序与 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(),用于路由路径。
  • 一个新类,express.Router,用于创建可模块化挂载的路由处理器。

app.route() 方法

🌐 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

🌐 express.Router class

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

🌐 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.

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

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

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;

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

🌐 Then, load the router module in the app:

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

其他变化

🌐 Other changes

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

🌐 The following table lists other small but important changes in Express 4:

ObjectDescription
Node.jsExpress 4 requires Node.js 0.10.x or later and has dropped support for Node.js 0.8.x.
http.createServer()

The http module is no longer needed, unless you need to directly work with it (socket.io/SPDY/HTTPS). The app can be started by using the app.listen() function.

app.configure()

The app.configure() function has been removed. Use the process.env.NODE_ENV or app.get('env') function to detect the environment and configure the app accordingly.

json spaces

The json spaces application property is disabled by default in Express 4.

req.accepted()

Use req.accepts(), req.acceptsEncodings(), req.acceptsCharsets(), and req.acceptsLanguages().

res.location()No longer resolves relative URLs.
req.paramsWas an array; now an object.
res.localsWas a function; now an object.
res.headerSentChanged to res.headersSent.
app.routeNow available as app.mountpath.
res.on('header')Removed.
res.charsetRemoved.
res.setHeader('Set-Cookie', val)

Functionality is now limited to setting the basic cookie value. Use res.cookie() for added functionality.

示例应用迁移

🌐 Example app migration

这是将 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应用

🌐 Version 3 app

app.js

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

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

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

随附的版本 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": "*"
}
}

过程

🌐 Process

通过安装 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:

Terminal window
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.errorHandler 不再在 express 对象上可用。你必须手动安装它们的替代方案并在应用中加载它们。
  2. 你不再需要加载 app.router 函数。它不是一个有效的 Express 4 应用对象,所以请移除 app.use(app.router); 代码。
  3. 确保中间件函数按正确的顺序加载——在加载应用路由后再加载 errorHandler

版本 4 应用

🌐 Version 4 app

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:

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),否则无需加载它,应用可以这样简单地启动:

🌐 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'));
});

运行应用

🌐 Run the app

迁移过程已完成,该应用现在是一个 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:

Terminal window
$ node .

加载 http://localhost:3000 并查看由 Express 4 渲染的首页。

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

升级到 Express 4 应用生成器

🌐 Upgrading to the Express 4 app generator

用于生成 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.

正在安装

🌐 Installing

如果你已经在系统上安装了 Express 3 应用生成器,你必须先卸载它:

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

Terminal window
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:

Terminal window
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.

应用生成器的更改

🌐 Changes to the app generator

命令选项和使用基本保持不变,以下情况除外:

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

  • 已移除 --sessions 选项。
  • 已移除 --jshtml 选项。
  • 添加了 --hogan 选项以支持 Hogan.js

示例

🌐 Example

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

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

Terminal window
$ 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:

Terminal window
$ 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.