一、安裝
複製代碼代碼如下:$ npm install express
或者在任何地方使用可執行的express(1) 安裝:
複製代碼代碼如下:/# 譯註:強烈建議這種方式
$ npm install -g express
二、快速上手
最快上手express 的方法是利用可執行的express(1) 來生成一個應用,如下所示:
創建一個app:
複製代碼代碼如下:
$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
安裝依賴包:
複製代碼代碼如下:
$ npm install -d
啟動服務器:
複製代碼代碼如下:
$ node app.js
三、創建一個服務器
要創建一個express.HTTPServer 實例,只需調用createServer() 方法。 通用這個應用實例,我們可以定義基於HTTP 動作(HTTP Verbs)的路由,以app.get() 為例:
複製代碼代碼如下:
var app = require('express').createServer();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
四、創建一個HTTPS 服務器
如上述初始化一個express.HTTPSServer 實例。然後我們給它傳一個配置對象,接受key、cert 和其他在https 文檔所提到的(屬性/方法)。
複製代碼代碼如下:
var app = require('express').createServer({ key: ... });
五、配置
Express 支持任意環境,如產品階段(production)和開發階段(development)。開發者可以使用configure() 方法來設置當前所需環境。如果configure() 的調用不包含任何環境名,它將運行於所有環境中所指定的回調。
譯註: 像production / development / stage 這些別名都是可以自已取的,如application.js 中的app.configure 所示。實際用法看下面例子。
下面這個例子僅在開發階段dumpExceptions (拋錯),並返回堆棧異常。不過在兩個環境中我們都使用methodOverride 和bodyParser。注意一下app.router 的使用,它可以(可選)用來加載(mount)程序的路由,另外首次調用app.get()、app.post() 等也將會加載路由。
複製代碼代碼如下:
app.configure(function(){
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(app.router);
});
app.configure('development', function(){
app.use(express.static(__dirname + '/public'));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
var oneYear = 31557600000;
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
app.use(express.errorHandler());
});
對於相似的環境你可以傳遞多個環境字符串:
複製代碼代碼如下:
app.configure('stage', 'prod', function(){
// config
});
對於任何內部設置(#),Express 提供了set(key[, val])、 enable(key) 和disable(key) 方法:
譯註:設置詳見:application.js 的app.set。
複製代碼代碼如下:
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('views');
// => "/absolute/path/to/views"
app.enable('some feature');
// 等價於:app.set('some feature', true);
app.disable('some feature');
// 等價於:app.set('some feature', false);
app.enabled('some feature')
// => false
});
變更環境我們可以設置NODE_ENV 環境變量,如:
複製代碼代碼如下:
$ NODE_ENV=production node app.js
這非常重要,因為多數緩存機制只在產品階段是被打開的。
六、設置
Express 支持下列快捷(out of the box)設置:
1.basepath 用於res.redirect() 的應用程序基本路徑(base path),顯式地處理綁定的應用程序(transparently handling mounted apps.)
2.view View 默認的根目錄為CWD/views
3.view engine 默認View 引擎處理(View 文件)並不需要使用後綴
4.view cache 啟用View 緩存(在產品階段被啟用)
5.charet 改變編碼,默認為utf-8
6.case sensitive routes 路由中區分大小寫
7.strit routing 啟用後(路由中的)結尾/ 將不會被忽略(譯註:即app.get('/sofish') 和app.get('/sofish/') 將是不一樣的)
8.json callback 啟用res.send() / res.json() 顯式的jsonp 支持(transparent jsonp support)
七、路由
Express 利用HTTP 動作提供一套提示性強、有表現力的路由API。打個比方,如果想要處理某個路徑為/user/12 的賬號,我們能像下面這樣來定義路由。關聯到命名佔位符(named placeholders)的值可用req.params 來訪問。
複製代碼代碼如下:
app.get('/user/:id', function(req, res){
res.send('user ' + req.params.id);
});
路由是一個在內部被編譯為正則的字符串。譬如,當/user/:id 被編譯,一個簡化版本的正則表達弄大概如下:
複製代碼代碼如下:
// 修改一下官方的這個字符串
///user//([^//]+)//?/
正則表達式可以傳入應用於復雜的場景。由於通過字面量正則表達式捕獲的內容組是匿名的,我們可能直接通過req.params 來訪問它們。因此,我們捕獲的第一組內容將是req.params[0],同時第二組是緊接著的req.params[1]。
複製代碼代碼如下:
app.get(/^//users?(?://(/d+)(?:/./.(/d+))?)?/, function(req, res){
res.send(req.params);
});
Curl 針對上述定義路由的請求:
複製代碼代碼如下:
$ curl http://dev:3000/user
[null,null]
$ curl http://dev:3000/users
[null,null]
$ curl http://dev:3000/users/1
["1",null]
$ curl http://dev:3000/users/1..15
["1","15"]
下面是一些路由的實例,關聯到他們可能使用到的路徑:
複製代碼代碼如下:
"/user/:id"
/user/12
"/users/:id?"
/users/5
/users
"/files/*"
/files/jquery.js
/files/javascripts/jquery.js
"/file/*.*"
/files/jquery.js
/files/javascripts/jquery.js
"/user/:id/:operation?"
/user/1
/user/1/edit
"/products.:format"
/products.json
/products.xml
"/products.:format?"
/products.json
/products.xml
/products
"/user/:id.:format?"
/user/12
/user/12.json
舉個例子,我們可以使用POST 發送json 數據,通過bodyParser 這個可以解析json 請求內容(或者其他內容)的中間件來返回數據,並將返回結果存於req.body 中:
複製代碼代碼如下:
var express = require('express')
, app = express.createServer();
app.use(express.bodyParser());
app.post('/', function(req, res){
res.send(req.body);
});
app.listen(3000);
通常我們可以使用一個像user/:id 這樣,沒有(命名)限制的“傻瓜”式的佔位符。然而比方說,我們要限制用戶id 只能是數字,那麼我們可能使用/user/:id([0-9]+),這個將僅當佔位符是包含至少一位數字時才生效(適配,match)。
八、進路控制(Passing Route Control)
我們可以通過調用第三個參數,next() 函數,來控制下一個適配的路由。如果找不到適配,控制權將會傳回給Connect,同時中間件將會按在use() 中添加的順序被依次調用。道理同樣適應於多個定義到同一路徑的路由,他們將會依次被調用直到其中某個不調用next() 而決定做出請求響應。
複製代碼代碼如下:
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
app.get('/users', function(req, res){
// do something else
});
app.all() 方法只調用一次就可以方便地把同樣的邏輯到所有HTTP 動作。下面我們使用它來從偽數據中提取一個用戶,將其賦給req.user。
複製代碼代碼如下:
var express = require('express')
, app = express.createServer();
var users = [{ name: 'tj' }];
app.all('/user/:id/:op?', function(req, res, next){
req.user = users[req.params.id];
if (req.user) {
next();
} else {
next(new Error('cannot find user ' + req.params.id));
}
});
app.get('/user/:id', function(req, res){
res.send('viewing ' + req.user.name);
});
app.get('/user/:id/edit', function(req, res){
res.send('editing ' + req.user.name);
});
app.put('/user/:id', function(req, res){
res.send('updating ' + req.user.name);
});
app.get('*', function(req, res){
res.send(404, 'what???');
});
app.listen(3000);
九、中間件
使用的Connect 中間件(屬性)通常伴隨著你的一個常規Connect 服務器,被傳到express.createServer() 。如:
複製代碼代碼如下:
var express = require('express');
var app = express.createServer(
express.logger()
, express.bodyParser()
);
另外,在configure() 塊內―― 這個漸進式的宮殿(譯註:笑^^,in a progressive manner),我們還可以方便地使用use() 來添加中間件。
複製代碼代碼如下:
app.use(express.logger({ format: ':method :url' }));
通常,使用connect 中間件你可能會用到require('connect'),像這樣:
複製代碼代碼如下:
var connect = require('connect');
app.use(connect.logger());
app.use(connect.bodyParser());
這在某種程度上來說有點不爽,所以express 重導出(re-exports)了這些中間件屬性,儘管他們是一樣的:
複製代碼代碼如下:
app.use(express.logger());
app.use(express.bodyParser());
中間件的順序非常重要,當Connect 收到一個請求,我們傳到createServer() 或者use() 執行的第一個中間件將附帶三個參數,request、response,以及一個回調函數(通常是next)。當next() 被調用,將輪到第二個中間件,依此類推。之所以說這是值得注意的,是因為很多中間件彼此依賴,例如methodOverride() 查詢req.body 方法來檢測HTTP 方法重載,另一方面bodyParser() 解析請求內容並將其於寄存於req.body。另一個例子是cookie 解析和session 支持,我們必須先use() cookieParser() 緊接著session()。
很多Express 應用都包含這樣的一行app.use(app.router),這看起來可能有點奇怪,其實它僅僅是一個包含所有定義路由規則,並執行基於現有URL 請求和HTTP 方法路由查找的一個中間件功能。 Express 允許你決定其位置(to position),不過默認情況下它被放置於底部。通過改變路由的位置,我們可以改變中間件的優先級,譬如我們想把錯誤報告做為最後的中間件,以便任何傳給next() 的異常都可以通過它來處理;又或者我們希望靜態文件服務優先級更低,以允許我們的路由可以監聽單個靜態文件請求的下載次數,等等。這看起來差不多是這樣的:
複製代碼代碼如下:
app.use(express.logger(...));
app.use(express.bodyParser(...));
app.use(express.cookieParser(...));
app.use(express.session(...));
app.use(app.router);
app.use(express.static(...));
app.use(express.errorHandler(...));
首先我們添加logger(),它可能包含node 的req.end() 方法,提供我們響應時間的數據。接下來請求的內容將會被解析(如果有數據的話),緊接著的是cookie 解析和session 支持,同時req.session 將會在觸發app.router 中的路由時被定義,這時我們並不調用next(),因此static() 中間件將不會知道這個請求,如若已經定義瞭如下一個路由,我們則可以記錄各種狀態、拒絕下載和消耗下載點數等。
複製代碼代碼如下:
var downloads = {};
app.use(app.router);
app.use(express.static(__dirname + '/public'));
app.get('/*', function(req, res, next){
var file = req.params[0];
downloads[file] = downloads[file] || 0;
downloads[file]++;
next();
});
十、路由中間件
路由可以利用路由器中間件,傳遞一個以上的回調函數(或者數組)到其方法中。這個特性非常有利於限制訪問、通過路由下載數據,等等。
通常異步數據檢索看起來可能像下例,我們使用:id 參數,嘗試加載一個用戶:
複製代碼代碼如下:
app.get('/user/:id', function(req, res, next){
loadUser(req.params.id, function(err, user){
if (err) return next(err);
res.send('Viewing user ' + user.name);
});
});
為保證DRY 原則和提升可讀,我們可以把這個邏輯應用於一個中間件內。如下所示,抽像這個邏輯到中間件內將允許你重用它,同時保證了我們路由的簡潔。
複製代碼代碼如下:
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
多重路由可以,並按順序應用到更深一層的邏輯,如限制一個用戶賬號的訪問。下面的例子只允許通過鑑定的用戶才可以編輯他(她)的賬號。
複製代碼代碼如下:
function andRestrictToSelf(req, res, next) {
req.authenticatedUser.id == req.user.id
? next()
: next(new Error('Unauthorized'));
}
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' + req.user.name);
});
時刻銘記路由只是簡單的函數,如下所示,我們可以定義返回中間件的函數以創建一個更具表現力,更靈活的方案。
複製代碼代碼如下:
function andRestrictTo(role) {
return function(req, res, next) {
req.authenticatedUser.role == role
? next()
: next(new Error('Unauthorized'));
}
}
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' + req.user.name);
});
常用的中間件“堆棧”可以通過一個數組來傳遞(會被遞歸應用),這些中間件可以混著、匹配到任何層次(which can be mixed and matched to any degree)。
複製代碼代碼如下:
var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
, all = [a, b];
app.get('/foo', a, function(){});
app.get('/bar', a, function(){});
app.get('/', a, middleware3, middleware4, function(){});
app.get('/', a, b, function(){});
app.get('/', all, function(){});
對於這個實例的完整代碼,請看route middleware example 這個倉庫。
我們可能會有多次想要“跳過”剩餘的路由中間件,繼續匹配後續的路由。做到這點,我們只需調用next() 時帶上'route' 字符串―― next('route')。如果沒有餘下的路由匹配到請求的URL,Express 將會返回404 Not Found。
十一、HTTP 方法
至此已接觸了好幾次app.get(),除此這外Express 還提供了其他常見的HTTP 動作,如app.post() 、app.del() 等等。
POST 用法的一個常用例子是提交一個表單。下面我們簡單地在html 中把表單的method 屬性設置為post,控制權將會指派給它下面所定義的路由。
複製代碼代碼如下:
<form method="post" action="/">
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
默認上Express 並不知道如何處理這個請求的內容,因此我們必須添加bodyParser 中間件,它將解析application/x-www-form-urlencoded 和application/json 請求的內容,並把變量存放於req.body 中。我們可以像下述示例一樣來使用這個中間件:
複製代碼代碼如下:
app.use(express.bodyParser());
如下,我們的路由將有權訪問req.body.user 對象,當有name 和email 被定義時它將包含這兩個屬性(譯註:如果表單發送的內容不為空的話)。
複製代碼代碼如下:
app.post('/', function(req, res){
console.log(req.body.user);
res.redirect('back');
});
當想在一個表單中使用像PUT 這樣的方法,我們可以使用一個命名為_method 的hidden input,它可以用以修改HTTP 方法。為了做這個,我們首先需要methodOverride 中間件,它必須出現於bodyParser 後面,以便使用它的req.body中所包含的表單值。
複製代碼代碼如下:
app.use(express.bodyParser());
app.use(express.methodOverride());
對於這些方法為何不是默認擁有,簡單來說只是因為它並不是Express 所要求完整功能所必須。方法的使用依賴於你的應用,你可能並不需要它們,客戶端依然能使用像PUT 和DELETE 這樣的方法,你可以直接使用它們,因為methodOverride 為form 提供了一個非常不錯的解決方案。下面將示範如何使用PUT 這個方法,看起來可能像:
複製代碼代碼如下:
<form method="post" action="/">
<input type="hidden" name="_method" value="put" />
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
app.put('/', function(){
console.log(req.body.user);
res.redirect('back');
});
十二、錯誤處理
Express 提供了app.error() 方法以便接收到的異常在一個路由里拋出,或者傳到next(err) 中。下面這個例子將基於特定的NotFound 異常處理不同的頁面:
複製代碼代碼如下:
function NotFound(msg){
this.name = 'NotFound';
Error.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
}
NotFound.prototype.__proto__ = Error.prototype;
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res){
throw new Error('keyboard cat!');
});
如下述,我們可以多次調用app.error()。這裡我們檢測NotFound 的實例,並顯示404 頁面,或者傳到next 錯誤處理器。值得注意的是這些處理器可以在任何地方定義,因為他們將會在listen() 的時候被放置於路由處理器下面。它允許在configure() 塊內有定義,以便我們能基於環境用不同的異常處理方式。
複製代碼代碼如下:
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
} else {
next(err);
}
});
為求簡潔(for the simplicity),這裡我們假定這個demo 的所有錯誤為500,當然你可以可以選擇自己喜歡的。像node 執行文件系統的系統調用時,你可能會接收到一個帶有ENOENT 的error.code,意思為“不存在這樣的文件或目錄” 的錯誤,我們可以在錯誤處理器中使用,或者當有需要時可顯示一個指定的頁面。
複製代碼代碼如下:
app.error(function(err, req, res){
res.render('500.jade', {
error: err
});
});
我們的app 同樣可以利用Connect 的errorHandler 中間件來匯報異常。譬如當我們希望在“開發” 環境輸出stderr 異常時,我們可以使用:
複製代碼代碼如下:
app.use(express.errorHandler({ dumpExceptions: true }));
同時在開發階段我們可能需要在花哨的HTML 頁面顯示我們傳遞和拋出的異常,對此我們可以把showStack 設置為true。
複製代碼代碼如下:
app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));
errorHandler 中間件還可以在Accept: application/json 存在的時候返回json,這對於開發重度依賴客戶端Javascript 的應用非常有用。
十三、Route 參數預處理
路由參數預處理,通過隱式數據加載和請求驗證,可以大大提升你程序的可讀性。打個比方,你通常需要持續地從多個路由獲取基本數據。像用/user/:id 加載一個用戶,通常來說我們可能會這樣幹:
複製代碼代碼如下:
app.get('/user/:userId', function(req, res, next){
User.get(req.params.userId, function(err, user){
if (err) return next(err);
res.send('user ' + user.name);
});
});
通過預處理,我們的參數可以映射到執行驗證、控制(coercion),甚至從數據庫加載數據的回調。如下我們帶著參數名調用app.param() 希望將其映射於某些中間件。如你所見,我們接受代表佔位符值的id 參數。使用這個,我們如常加載用戶並處理錯誤,以及簡單地調用next() 來把控制權交由下一個預處理或者路由處理器。
複製代碼代碼如下:
app.param('userId', function(req, res, next, id){
User.get(id, function(err, user){
if (err) return next(err);
if (!user) return next(new Error('failed to find user'));
req.user = user;
next();
});
});
一旦這樣做,上所述將會大大地提昇路由的可讀性,並且允許我們輕鬆地在整個程序中共享邏輯:
複製代碼代碼如下:
app.get('/user/:userId', function(req, res){
res.send('user ' + req.user.name);
});
十四、View 處理
View 文件件使用<name>.<engine> 這樣的格式,其中<engine> 是被require 進來模塊的名。例如layout.ejs 將告訴view 系統去require('ejs'),被加載的模塊必須(導出) exports.compile(str, options) 方法,並返回一個Function 來適應Express。 app.register() 可用以改變這種默認行為,將文件擴展名映射到特定的引擎。譬如“foo.html” 可以由ejs 來處理。
下面這個例子使用Jade 來處理index.html。因為我們並未使用layout: false,index.jade 處理後的內容將會被傳入到layout.jade 中一個名為body 的本地變量。
複製代碼代碼如下:
app.get('/', function(req, res){
res.render('index.jade', { title: 'My Site' });
});
新的view engine 設置允許我們指定默認的模板引擎,例如當我們使用jade 時可以這樣設置:
複製代碼代碼如下:
app.set('view engine', 'jade');
允許我們這樣處理:
複製代碼代碼如下:
res.render('index');
對應於:
複製代碼代碼如下:
res.render('index.jade');
當view engine 被設定,擴展名實屬可選,但我們依然可以混著匹配模板引擎:
複製代碼代碼如下:
res.render('another-page.ejs');
Express 同時還提供了view options 設置,這將應用於一個view 每次被渲染的時候,譬如你不希望使用layouts 的時候可能會這樣做:
複製代碼代碼如下:
app.set('view options', {
layout: false
});
在需要的時候,這可以在res.render() 調用的內部進行重載:
複製代碼代碼如下:
res.render('myview.ejs', { layout: true });
當有需要變更一個layout,我們通常需要再指定一個路徑。譬如當我們已經把view engine 設置為jade,並且這個文件命名為./views/mylayout.jade,我們可以這樣簡單地進行傳參:
複製代碼代碼如下:
res.render('page', { layout: 'mylayout' });
否則(譯註:沒有把view engine 設置為jade 或者其他的引擎時),我們必須指定一個擴展名:
複製代碼代碼如下:
res.render('page', { layout: 'mylayout.jade' });
它們同樣可以是絕對路徑:
複製代碼代碼如下:
res.render('page', { layout: __dirname + '/../../mylayout.jade' });
對於這點有一個不錯的例子―― 自定義ejs 的起始和閉合標籤:
複製代碼代碼如下:
app.set('view options', {
open: '{{',
close: '}}'
})
十五、View 部件
Express 的view 系統內置了部件(partials) 和集合器(collections)的支持,相當於用一個“迷你” 的view 替換一個文檔碎片(document fragment)。示例,在一個view 中重複渲染來顯示評論,我們可以使用部件集:
複製代碼代碼如下:
partial('comment', { collection: comments });
如果並不需要其他選項或者本地變量,我們可以省略整個對象,簡單地傳進一個數組,這與上述是等價的:
複製代碼代碼如下:
partial('comment', comments);
在使用中,部件集無償地提供了一些“神奇” 本地變量的支持:
1.firstInCollection true,當它是第一個對象的時候
2.indexInCollection 在集合器對像中的索引
3.lastInCollection true,當它是最後一個對象的時候
4.collectionLength 集合器對象的長度
本地變量的傳遞(生成)具備更高的優先級,同時,傳到父級view 的本地變量對於子級view 同樣適應。例如當我們用partial('blog/post', post) 來渲染一個博客文章,它將會生成一個post 本地變量,在調用這個函數的view 中存在本地變量user,它將同樣對blog/post 有效。 (譯註:這裡partial 比較像php 中的include 方法)。
注意: 請謹慎使用部件集合器,渲染一個長度為100 的部件集合數組相當於我們需要處理100 個view。對於簡單的集合,最好重複內置,而非使用部件集合器以避免開銷過大。
十六、View 查找
View 查找相對於父級view (路徑)執行,如我們有一個view 頁面叫作views/user/list.jade,並且在其內部寫有partial('edit') 則它會嘗試加載views/user/edit.jade,同理partial('../messages') 將會加載views/messages.jade。
View 系統還支持模板索引,允許你使用一個與view 同名的目錄。例如在一個路由中,res.render('users') 得到的非views/users.jade 即views/users/index.jade。 (譯註:先處理<path>.<engine> 的情況,再處理<path>/<index.<engine> 的情況,詳情可見view.js。)
當使用上述view 索引,我們在與view 同一個目錄下,使用partial('users') 中引用views/users/index.jade,與此同時view 系統會嘗試索引../users/index,而無須我們調用partial('users')。
十七、Template Engines
下列為Express 最常用的模板引擎:
1.Haml:haml 實現
2.Jade:haml.js 繼位者
3.EJS:嵌入式JavaScript
4.CoffeeKup:基於CoffeeScript 的模板
5.jQuery Templates
十八、Session 支持
Session 支持可以通過使用Connect 的session 中間件來獲得,為此通常我們同時需要在其前加上cookieParser 中間件,它將解析和存儲cookie 數據於req.cookies 中。
複製代碼代碼如下:
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));
默認情況下session 中間件使用Connect 內置的內存存儲,然而還有其他多種實現方式。如connect-redis 提供了一種Redis 的session 存儲,它這可像下面這樣被使用:
複製代碼代碼如下:
var RedisStore = require('connect-redis')(express);
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
至此,req.session 和req.sessionStore 屬性將可以被所有路由和後繼的中間件使用。在req.session 上的所有屬性都會在一個響應中被自動保存下來,譬如當我們想要添加數據到購物車:
複製代碼代碼如下:
var RedisStore = require('connect-redis')(express);
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
app.post('/add-to-cart', function(req, res){
// 我們可能通過一個表單POST 出多個item
// (在些使用bodyParser() 中間件)
var items = req.body.items;
req.session.items = items;
res.redirect('back');
});
app.get('/add-to-cart', function(req, res){
// 當返回時,頁面GET /add-to-cart
// 我們可以檢查req.session.items && req.session.items.length
// 來打印出提示
if (req.session.items && req.session.items.length) {
req.notify('info', 'You have %s items in your cart', req.session.items.length);
}
res.render('shopping-cart');
});
對於req.session 對旬,它還有像Session#touch()、Session#destroy()、 Session#regenerate() 等用以維護和操作session 的方法。更多的詳情請看Connect Session 的文檔。
十九、升級指南
對於使用Express 1.x 的同學,如果你有很重要的程序需要升級到2.x 以獲得更好的支持,請看官方非常詳細的遷移指南:http://expressjs.com/guide.html#migration-guide