迁移至 Express 4
Express 4 相对于 Express 3 是一个破坏性变更版本。 这意味着如果在依赖项中更新 Express 版本,现有的 Express 3 应用将无法正常运行。
本文包括:
- Changes in Express 4.
- An example of migrating an Express 3 app to Express 4.
- Upgrading to the Express 4 app generator.
Express 4 的变更
Express 4 中有几项重大变更:
- Changes to Express core and middleware system. The dependencies on Connect and built-in middleware were removed, so you must add middleware yourself.
- Changes to the routing system.
- Various other changes.
参阅:
Express 核心和中间件系统的变更
Express 4 不再依赖 Connect,并从核心中移除了所有内置中间件,express.static 函数除外。 这意味着 Express 现在是一个独立的路由与中间件 Web 框架,其版本和发布不再受中间件更新的影响。
由于不再包含内置中间件,你必须显式添加应用运行所需的全部中间件。 只需按照以下步骤操作:
- Install the module:
npm install --save \<module-name> - 在你的应用中,引入该模块:
require('module-name') - 按照其文档说明使用该模块:
app.use( ...)`
下表列出了 Express 3 中间件及其在 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 中间件的完整清单。
大多数情况下,你只需将旧版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 的路由文件,内容如下:
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;然后,在应用程序中加载路由模块:
var birds = require('./birds');
// ...
app.use('/birds', birds);import birds from './birds';
// ...
app.use('/birds', birds);应用现在能够处理 /birds 和 /birds/about 路径的请求,并会调用专用于该路由的 timeLog 中间件。
其他改变:
下表列出了 Express 4 中其他细微但重要的变更:
| Object | Description |
|---|---|
| Node.js | Express 4 requires Node.js 0.10.x 或更高版本,并且已经放弃了对 Node.js 0.8.x的支持。 |
http.createServer() | 不再需要 |
app.configure() |
|
json spaces | Express 4默认禁用了 “json spaces” 应用程序属性。 |
req.accepted() | 使用 |
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 值。 如需使用更多功能,请使用 |
应用迁移示例
以下是将 Express 3 应用迁移至 Express 4 的示例。
需要关注的文件是 app.js 和 package.json。
版本 3 应用
app.js
假设一个 Express 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 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
对应的 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 更新至各自的最新版本。
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 进行如下修改:
-
Express 内置中间件函数
express.favicon、express.logger、express.methodOverride、express.session、express.bodyParser和express.errorHandler已从express对象中移除。 你必须手动安装它们的替代模块,并在应用中引入加载。 -
你不再需要加载
app.router函数。 它并非有效的 Express 4 应用对象,因此请删除app.use(app.router);代码。 -
确保中间件函数按正确顺序加载——在应用路由加载完毕后再加载
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 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),否则无需加载该模块,应用可通过以下方式直接启动:
app.listen(app.get('port'), () => { console.log('Express server listening on port ' + app.get('port'));});运行应用
迁移流程已完成,当前应用已升级为 Express 4 应用。 若要确认,请使用以下命令启动应用:
$ node .访问 http://localhost:3000,查看由 Express 4 渲染的首页。
升级至 Express 4 应用生成器
用于生成 Express 应用的命令行工具仍为 express,但要升级至新版本,你必须先卸载 Express 3 应用生成器,然后安装全新的 express-generator。
安装
如果你的系统中已安装 Express 3 应用生成器,必须先将其卸载:
npm uninstall -g expressyarn global remove expresspnpm remove --global expressbun remove --global express根据你的文件和目录权限配置,你可能需要使用 sudo 运行该命令。
现在安装新版生成器:
npm install -g express-generatoryarn global add express-generatorpnpm add --global express-generatorbun add --global express-generator根据你的文件和目录权限配置,你可能需要使用 sudo 运行该命令。
现在,你系统中的 express 命令已更新为 Express 4 生成器。
更改应用生成器
命令选项和用法基本保持不变,仅存在以下例外:
- 已移除
--sessions选项。 - 移除了 “—jshtml” 选项。
- Added the
--hoganoption to support Hogan.js.
示例
执行以下命令来创建一个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 文件就是 ./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 无法独立启动的原因。