凡是都有 Part 2,所以這時候來一個 Part 2 也是很合理的(挺,上一次介紹了 expressjs, jade, socket.io,這一次來介紹一下有關於 Session 的部份,以及如何與資料庫做結合。
首先,你必須知道什麼叫做 Session,以及什麼叫做資料庫。
Session? Database?
RTFM
TL;DR
Session in NodeJS
其實 NodeJS 並沒有特別實做 Session 這個項目,所以第三方套件就有相當多的選擇,
- Express
- connect
- connect-redis
- cookie-sessions
- DO IT BY YOURSELF
上述的 Express 與 connect 其實是講同一件事情,Express 本身是使用 Connect (Connect Middleware Framework) 所提供的方法,所以以下我會以 Connect 來稱呼他們。雖然我很不想講,不過關於 Connect 所提供的 Session 可以去看官方的介紹。
然而,connect-redis 其實也是 Connect 的一個延伸,由於 Connect 所儲存 Session 的方式,是把他儲存在記憶體中(MemoryStore),這是預設的。而這個第三方套件,是可以將 Session 儲存在 Redis 這個資料庫中。關於 Redis 我們往後再做介紹,當然也有 connect-mongo 可以用就是了。
最後,你也可以用前陣子很流行的 Cookie base 的儲存方式,來放你的 Session。cookie-sessions 就是一個第三方套件,用於將 Session 放在 Cookie 裡面,不過,他需要用到 Connect,而有趣的地方是,Connect 本身也提供了 Cookie session middleware,所以要怎麼用就見仁見智。
DO IT BY YOURSELF, 如果真的那麼想做,那這裡有一篇文章可以參考一下,
Session in Connect
既然人家都把輪子準備好了,那麼我們直接拿來滾也是很合理的。
(function() {
var app, express;
express = require('express');
app = express.createServer();
app.configure(function() {
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#"
}));
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
然後我們執行了一下,服務正常啟動了,但是瀏覽器連線的時候,馬上出了錯誤!
$ node test.js
Express server listening on port 3000 in development mode
TypeError: Cannot read property 'connect.sid' of undefined
at Object.session [as handle] (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/middleware/session.js:299:32)
at next (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:203:15)
at Object.handle (/home/hinablue/node-in-session/node_modules/express/lib/http.js:83:5)
at next (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:203:15)
at HTTPServer.handle (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:216:3)
at HTTPServer.emit (events.js:70:17)
at HTTPParser.onIncoming (http.js:1514:12)
at HTTPParser.onHeadersComplete (http.js:102:31)
at Socket.ondata (http.js:1410:22)
at TCP.onread (net.js:374:27)
少了什麼東西嗎?是的,依照 Express 官方說明,我們還需要 cookieParser()
這個東西,所以我們把他加上去,
(function() {
var app, express;
express = requi/home/hinablue/workspace/works/jsdc/re('express');
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser());
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#"
}));
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
然後我們再試一次,就正常運作了呢,接下來我們做一個 / 來讓瀏覽器可以顯示一點東西,
(function() {
var app, express;
express = require('express');
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser());
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#"
}));
});
app.get("/", function(req, res) {
console.info(req.session.item);
req.session.item = 'Hello World';
return res.send('Hello World');
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
然後我們執行之後,打開瀏覽器拜訪一下 Hello World
,
$ node test.js
Express server listening on port 3000 in development mode
undefined
Hello World
你會發現,我們第一次訪問的時候,session.item
是空值(undefined),而第二次訪問的時候,session.item
已經變為 Hello World 了。這樣是一個簡易的 Session 在 Express 當中的操作。如果你需要更多操作的方法,可以查看官方的文件說明。
Session in MongoDB
在 Express 當中,Session 預設儲存的方式是記憶體。如果你要儲存在資料庫中也是可以的,關於資料庫呢,這邊除了 MongoDB 之外,還有另外一個,叫做 Redis。他與 MongoDB 一樣是 key/value 的資料庫。差別大概在他有 VMware sponsored 這樣吧(喂
由於先前我們已經介紹過 MongoDB,所以這邊先以 MongoDB 的儲存方式來介紹。雖然 Connect 推薦使用 Redis 來作儲存媒介,不過也沒硬性規定就是。
BUT!
原生的寫法是行不通的,
(function() {
var app, express, mongo, mongoServer;
express = require('express');
mongo = require('mongodb');
mongoServer = new mongo.Server('localhost', 27017, {
auto_reconnect: true,
pooSize: 5
});
/home/hinablue/workspace/works/jsdc/
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser());
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#",
store: new mongo.Db('mysession', mongoServer)
}));
});
app.get("/", function(req, res) {
console.info(req.session.item);
req.session.item = 'Hello World';
return res.send('Hello World');
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
馬上就會出現錯誤,
$ node test.js
Express server listening on port 3000 in development mode
TypeError: Object #<Db> has no method 'get'
at Object.session [as handle] (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/middleware/session.js:318:11)
at next (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:203:15)
at Object.cookieParser [as handle] (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js:44:5)
at next (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:203:15)
at Object.handle (/home/hinablue/node-in-session/node_modules/express/lib/http.js:83:5)
at next (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:203:15)
at HTTPServer.handle (/home/hinablue/node-in-session/node_modules/express/node_modules/connect/lib/http.js:216:3)
at HTTPServer.emit (events.js:70:17)
at HTTPParser.onIncoming (http.js:1514:12)
at HTTPParser.onHeadersComplete (http.js:102:31)
不要問我為什麼,這個問題是 mongodb-native 中的動作,跟 Connect 中的作法不同所導致。所以,請使用 connect-mongo,
(function() {
var app, express, mongoStore;
express = require('express');
mongoStore = require('connect-mongo')(express);
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser());
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#",
store: new mongoStore({
host: 'localhost',
port: 27017,
db: 'mysessions',
collection: 'sessions'
})
}));
});
app.get("/", function(req, res) {
console.info(req.session.item);
req.session.item = 'Hello World';
return res.send('Hello World');
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
然後我們從 console 進去 mongo 查看一下,
$ mongo
MongoDB shell version: 2.0.4
connecting to: test
> show dbs
local (empty)
mysessions 0.203125GB
> use mysessions
switched to db mysessions
> show collections
sessions
system.indexes
> db.sessions.find()
{ "_id" : "vB4Gd1rZJQ8e6s0rbE0rEqrR.c0VnxIUGAxsQfi9zA2XoQ9lV9oIs5ULsn+Y/99wu1pI", "session" : "{\"lastAccess\":1334752463452,\"cookie\":{\"originalMaxAge\":14400000,\"expires\":\"2012-04-18T16:34:23.452Z\",\"httpOnly\":true,\"path\":\"/\"},\"item\":\"Hello World\"}", "expires" : ISODate("2012-04-18T16:34:23.452Z") }
>
這樣你就看到我們剛剛存的東西了。
Session in Redis
另外就是 Redis 了,首先要安裝,
$ sudo apt-get install redis-server
然後我們改寫一下,
(function() {
var app, express, redisStore;
express = require('express');
redisStore = require('connect-redis')(express);
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser());
return this.use(express.session({
secret: "@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#",
store: new redisStore()
}));
});
app.get("/", function(req, res) {
console.info(req.session.item);
req.session.item = 'Hello World';
return res.send('Hello World');
});
app.listen(3000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in " + process.env.NODE_ENV + " mode");
});
}).call(this);
然後用瀏覽器跑一下,我們再用 redis-cli 來看看儲存的結果,
$ node test.js
Express server listening on port 3000 in development mode
undefined
Hello World
$ redis-cli
redis 127.0.0.1:6379> KEYS *
1) "sess:8nQiHfxTHfouhSaB5tgj1cQY.UP0VLd1qOneh0D30H2+ivDyYLlX/GcXLdIl9dczWAlg"
redis 127.0.0.1:6379> MGET "sess:8nQiHfxTHfouhSaB5tgj1cQY.UP0VLd1qOneh0D30H2+ivDyYLlX/GcXLdIl9dczWAlg"
1) "{\"lastAccess\":1334753540196,\"cookie\":{\"originalMaxAge\":14400000,\"expires\":\"2012-04-18T16:52:20.197Z\",\"httpOnly\":true,\"path\":\"/\"},\"item\":\"Hello World\"}"
redis 127.0.0.1:6379> exit
這樣就會發現,我們剛剛所儲存的 Session 了!
Session in Cookie
最後我們來說一下 Cookie base 的儲存方式。這個儲存方式有 Cookie 先天的限制,所以,如果你的 Session 所儲存的內容過多(大於 4kb),或是你有些敏感的資料的話,是不建議使用這種方式來儲存的。
(function() {
var app, connect, express;
connect = require('connect');
express = require('express');
app = express.createServer();
app.configure(function() {
this.use(express.cookieParser('@#$TYHBVGHJIY^TWEYKJHNBGFDWGHJKUYTWE#$%^&*&^%$#'));
return this.use(express.cookieSession());
});
app.get("/", function(req, res) {
console.info(req.session.item);
req.session.item = 'Hello World';
return res.send('Hello World');
});
app.get("/page2", function(req, res) {
console.log(req.session.item);
return res.send(req.session.item);
});
app.listen(5000, function() {
return console.info("Express server listening on port " + (this.address().port) + " in mode");
});
}).call(this);
執行結果呢?
$ node test.js
Express server listening on port 3000 in development mode
Hello World
Hello World
你會發現,我一打開瀏覽器就會出現 Session 的資料了,為什麼?因為這是用 Cookie 儲存的關係,所以不要問我為什麼。另外,注意! express.cookieParser()
裡面的 secret
一定要寫,不然會錯誤喔!
我這裡貼上 Header 的結果給大家參考一下,
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:UTF-8,*;q=0.5
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Cookie:connect.sid=dj5sq4si22fODOemcEpTrGzT.h%2BHj4JNtvtQpJp%2BA6pUdh1xiEzTv0hoZH%2BLy%2FKr9%2F1g; connect.sess=j%3A%7B%22item%22%3A%22Hello%20World%2C%20asdjfkla%3Bdfja%3Blskdfja%3Bsldkfja%3Bsldfjas%3Bldfja%3Bsldfkjas%3Bldfja%3Bsldfjka%3Bdlfja%3Bsldfjka%3Bsldfja%3Bldkjfa%3Bsldfj%22%7D.41ZpYhK3SpKO5CxWXwa1eJlODvp%2Fj1%2BE9a0dbFL9emk
Host:localhost:5000
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.24 Safari/536.5
請注意 Cookie 的部份,這個部份有 connect.sess
這是 Connect 預設的,裡面是加密過的資料,當你所儲存的 Session 越多時,這個東西就會越來越長。如果過長會炸掉,就這樣!
結語
Session 還是用 DB 來存會好一點,就這樣啦(飄走