迁移到 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:
- 对 Express 核心和中间件系统的更改。对 Connect 和内置中间件的依赖已被移除,因此你必须自己添加中间件。
- 路由系统的更改。
- 其他各种更改。
另请参见:
🌐 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:
- 安装模块:
npm install --save <module-name> - 在你的应用中,引用模块:
require('module-name') - 按照文档使用该模块:
app.use( ... )
下表列出了 Express 3 的中间件及其在 Express 4 中的对应项。
🌐 The following table lists Express 3 middleware and their counterparts in Express 4.
| Express 3 | Express 4 |
|---|---|
express.bodyParser | |
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 接受参数
🌐 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:
var express = require('express');var router = express.Router();
// middleware specific to this routerrouter.use((req, res, next) => { console.log('Time: ', Date.now()); next();});// define the home page routerouter.get('/', (req, res) => { res.send('Birds home page');});// define the about routerouter.get('/about', (req, res) => { res.send('About birds');});
module.exports = router;import express from 'express';
const router = express.Router();
// middleware specific to this routerrouter.use((req, res, next) => { console.log('Time: ', Date.now()); next();});// define the home page routerouter.get('/', (req, res) => { res.send('Birds home page');});// define the about routerouter.get('/about', (req, res) => { res.send('About birds');});
export default router;然后,在应用中加载路由模块:
🌐 Then, load the router module in the app:
var birds = require('./birds');
// ...
app.use('/birds', birds);import birds from './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:
| Object | Description |
|---|---|
| Node.js | Express 4 requires Node.js 0.10.x or later and has dropped support for Node.js 0.8.x. |
http.createServer() | The |
app.configure() | The |
json spaces | The |
req.accepted() | Use |
res.location() | No longer resolves relative URLs. |
req.params | Was an array; now an object. |
res.locals | Was a function; now an object. |
res.headerSent | Changed to res.headersSent. |
app.route | Now available as app.mountpath. |
res.on('header') | Removed. |
res.charset | Removed. |
res.setHeader('Set-Cookie', val) | Functionality is now limited to setting the basic cookie value. Use |
示例应用迁移
🌐 Example app migration
这是将 Express 3 应用迁移到 Express 4 的示例。 相关的文件是 app.js 和 package.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:
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 environmentsapp.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 onlyif (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'));});import express from 'express';import routes from './routes';import user from './routes/user';import http from 'http';import path from 'path';
const app = express();
// all environmentsapp.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 onlyif (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:
npm install serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latest --saveyarn add serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latestpnpm add serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latestbun add serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest pug@latest对 app.js 做以下更改:
🌐 Make the following changes to app.js:
- 内置的 Express 中间件函数
express.favicon、express.logger、express.methodOverride、express.session、express.bodyParser和express.errorHandler不再在express对象上可用。你必须手动安装它们的替代方案并在应用中加载它们。 - 你不再需要加载
app.router函数。它不是一个有效的 Express 4 应用对象,所以请移除app.use(app.router);代码。 - 确保中间件函数按正确的顺序加载——在加载应用路由后再加载
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:
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 environmentsapp.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 routesif (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'));});import http from 'http';import express from 'express';import routes from './routes';import user from './routes/user';import path from 'path';import favicon from 'serve-favicon';import logger from 'morgan';import methodOverride from 'method-override';import session from 'express-session';import bodyParser from 'body-parser';import multer from 'multer';import errorHandler from 'errorhandler';
const app = express();
// all environmentsapp.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 routesif (app.get('env') === 'development') { app.use(errorHandler());}
const 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:
$ 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:
npm uninstall -g expressyarn global remove expresspnpm remove --global expressbun remove --global 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-generatoryarn global add express-generatorpnpm add --global express-generatorbun add --global 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:
$ 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.