Express 4 是對(duì) Express 3 的一個(gè)顛覆性改變,也就是說如果您更新了 Express, Express 3 應(yīng)用會(huì)無法工作。
該章包含如下內(nèi)容:
Express 4 的主要變化如下:
其他變化請(qǐng)參考:
Express 4 不再依賴 Connect,而且從內(nèi)核中移除了除 express.static 外的所有內(nèi)置中間件。也就是說現(xiàn)在的 Express 是一個(gè)獨(dú)立的路由和中間件 Web 框架,Express 的版本升級(jí)不再受中間件更新的影響。
移除了內(nèi)置的中間件后,您必須顯式地添加所有運(yùn)行應(yīng)用需要的中間件。請(qǐng)遵循如下步驟:
npm install --save <module-name>require('module-name')app.use( ... )下表列出了 Express 3 和 Express 4 中對(duì)應(yīng)的中間件。
| Express 3 | Express 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 的所有中間件列表。
多數(shù)情況下,您可以直接使用 Express 4 中對(duì)應(yīng)的中間件替換 Express 3 中的中間件,請(qǐng)參考 GitHub 中的模塊文檔了解更多信息。
app.use accepts parameters
在 Express 4 中,可以從路由句柄中讀取參數(shù),以該參數(shù)的值作為路徑加載中間件,比如像下面這樣:
app.use('/book/:id', function(req, res, next) {
console.log('ID:', req.params.id);
next();
});
應(yīng)用現(xiàn)在隱式地加載路由中間件,因此不需要擔(dān)心中間件加載順序。
定義路由的方式依然未變,但是新的路由系統(tǒng)有兩個(gè)新功能能幫助您組織路由:
app.route() 可以為路由路徑創(chuàng)建鏈?zhǔn)铰酚删浔?/li>
express.Router 可以創(chuàng)建可掛載的模塊化路由句柄。app.route() 方法
新增加的 app.route() 方法可為路由路徑創(chuàng)建鏈?zhǔn)铰酚删浔S捎诼窂皆谝粋€(gè)地方指定,會(huì)讓路由更加模塊化,也能減少代碼冗余和拼寫錯(cuò)誤。請(qǐng)參考 Router() 文檔獲取更多關(guān)于路由的信息。
下面是一個(gè)使用 app.route() 方法定義鏈?zhǔn)铰酚删浔睦印?/p>
app.route('/book')
.get(function(req, res) {
res.send('Get a random book');
})
.post(function(req, res) {
res.send('Add a book');
})
.put(function(req, res) {
res.send('Update the book');
});
express.Router 類
另外一個(gè)幫助組織路由的是新加的 express.Router 類,可使用它創(chuàng)建可掛載的模塊化路由句柄。Router 類是一個(gè)完整的中間件和路由系統(tǒng),鑒于此,人們常稱之為“迷你應(yīng)用”。
下面的例子創(chuàng)建了一個(gè)模塊化的路由,并加載了一個(gè)中間件,然后定義了一些路由,并且在主應(yīng)用中將其掛載到指定路徑。
在應(yīng)用目錄下創(chuàng)建文件 birds.js,其內(nèi)容如下:
var express = require('express');
var router = express.Router();
// 特針對(duì)于該路由的中間件
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定義主頁路由
router.get('/', function(req, res) {
res.send('Birds home page');
});
// 定義 about 頁面路由
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
在應(yīng)用中加載該路由:
var birds = require('./birds');
...
app.use('/birds', birds);
應(yīng)用現(xiàn)在就可以處理發(fā)送到 /birds 和 /birds/about 的請(qǐng)求,并且會(huì)調(diào)用特針對(duì)于該路由的 timeLog 中間件。
下表列出了 Express 4 中其他一些盡管不大,但是非常重要的變化。
| 對(duì)象 | 描述 |
|---|---|
| Node | Express 4 需要 Node 0.10.x 或以上版本,已經(jīng)放棄了對(duì) Node 0.8.x 的支持。 |
|
|
http 模塊,除非您需要直接使用它(socket.io/SPDY/HTTPS),使用
|
|
|
已經(jīng)刪除 |
|
|
Express 4 默認(rèn)禁用 |
|
|
使用 |
|
|
不再解析相對(duì) URLs。 |
|
|
從數(shù)組變?yōu)閷?duì)象。 |
|
|
從函數(shù)變?yōu)閷?duì)象。 |
|
|
變?yōu)? |
|
|
變?yōu)? |
|
|
已刪除。 |
|
|
已刪除。 |
|
|
功能僅限于設(shè)置基本的 cookie 值,使用
|
下面是一個(gè)從 Express 3 遷移到 Express 4 的例子,請(qǐng)留意 app.js 和 package.json。
Express 3 應(yīng)用
app.js
請(qǐng)看如下 Express 3 應(yīng)用,其 app.js 文件內(nèi)容如下:
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var app = express();
// 環(huán)境
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
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')));
// 只為開發(fā)使用
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
package.json
相應(yīng)的 package.json 文件內(nèi)容如下:
{
"name": "application-name",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.12.0",
"jade": "*"
}
}
首先安裝 Express 4 應(yīng)用需要的中間件,使用如下命令將 Express 和 Jade 更新至最新版本:
$ npm install serve-favicon morgan method-override express-session body-parser multer errorhandler express@latest jade@latest --save
按如下方式修改 app.js 文件:
express.favicon、express.logger、express.methodOverride、express.session、express.bodyParser、express.errorHandler 這些內(nèi)置中間件在 express 對(duì)象中已經(jīng)沒有了,您必須手動(dòng)安裝相應(yīng)的中間件,并在應(yīng)用中加載它們。app.router,它不再是一個(gè)合法的 Express 4 對(duì)象,刪掉 app.use(app.router);。errorHandler。Express 4 應(yīng)用
package.json
運(yùn)行上述 npm 命令后,會(huì)將 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",
"jade": "^1.5.0",
"method-override": "^2.1.2",
"morgan": "^1.2.2",
"multer": "^0.1.3",
"serve-favicon": "^2.0.1"
}
}
app.js
刪掉非法的代碼,加載需要的中間件,再做一些必要的修改,新的 app.js 內(nèi)容如下:
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();
// 環(huán)境
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(favicon(__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);
// 加載路由完成后才能加載錯(cuò)誤處理中間件
if ('development' == app.get('env')) {
app.use(errorHandler());
}
var server = http.createServer(app);
server.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
除非需要直接使用 http 模塊(socket.io/SPDY/HTTPS),否則不必加載它,可使用如下方式啟動(dòng)應(yīng)用:
app.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
遷移完成后,應(yīng)用就變成了 Express 4 應(yīng)用。為了確保遷移成功,使用如下命令啟動(dòng)應(yīng)用:
$ node .
輸入 http://localhost:3000,即可看到經(jīng)由 Express 4 渲染的主頁。
生成 Express 應(yīng)用的命令行還是 express,為了升級(jí)到最新版本,您必須首先卸載 Express 3 的應(yīng)用生成器,然后安裝新的 express-generator。
如果您已經(jīng)安裝了 Express 3 應(yīng)用生成器,請(qǐng)使用如下命令卸載:
$ npm uninstall -g express
根據(jù)您的文件目錄權(quán)限,您可能需要以 sudo 權(quán)限執(zhí)行該命令。
然后安裝新的生成器:
$ npm install -g express-generator
根據(jù)您的文件目錄權(quán)限,您可能需要以 sudo 權(quán)限執(zhí)行該命令。
現(xiàn)在系統(tǒng)的 express 命令就升級(jí)為 Express 4 應(yīng)用生成器了。
大部分命令參數(shù)和使用方法都維持不變,除過如下選項(xiàng):
--sessions 選項(xiàng)。--jshtml 選項(xiàng)。--hogan 選項(xiàng)以支持 Hogan.js。運(yùn)行下述命令創(chuàng)建一個(gè) Express 4 應(yīng)用:
$ express app4
如果查看 app4/app.js 的內(nèi)容,會(huì)發(fā)現(xiàn)應(yīng)用需要的所有中間件(不包括 express.static)都作為獨(dú)立模塊載入,而且再不顯式地加載 router 中間件。
您可能還會(huì)發(fā)現(xiàn),和舊的生成器生成的應(yīng)用相比, app.js 現(xiàn)在成了一個(gè) Node 模塊。
安裝完依賴后,使用如下命令啟動(dòng)應(yīng)用:
$ npm start
如果看一看 package.json 文件中的 npm 啟動(dòng)腳本,會(huì)發(fā)現(xiàn)啟動(dòng)應(yīng)用的真正命令是 node ./bin/www,在 Express 3 中則為 node app.js。
Express 4 應(yīng)用生成器生成的 app.js 是一個(gè) Node 模塊,不能作為應(yīng)用(除非修改代碼)單獨(dú)啟動(dòng),需要通過一個(gè) Node 文件加載并啟動(dòng),這里這個(gè)文件就是 node ./bin/www。
創(chuàng)建或啟動(dòng) Express 應(yīng)用時(shí),bin 目錄或者文件名沒有后綴的 www 文件都不是必需的,它們只是生成器推薦的做法,請(qǐng)根據(jù)需要修改。
如果不想保留 www,想讓應(yīng)用變成 Express 3 的形式,則需要?jiǎng)h除 module.exports = app;,并在 app.js 末尾粘貼如下代碼。
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
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"。
現(xiàn)在就將 ./bin/www 的功能又改回到 app.js 中了。我們并不推薦這樣做,這個(gè)練習(xí)只是為了幫助大家理解 ./bin/www 是如何工作的,以及為什么 app.js 不能再自己?jiǎn)?dòng)。