摘要:簡介在我的前一篇小文中小書提到了可以更換會話儲存那么這篇文章我們就來講講在進行會話管理的時候如何將會話數據保存在外部數據庫中本文中我們使用用作會話儲存數據庫本文中使用的模塊以及版本號一覽模塊名稱版本號特性支持支持所有版本的支持支持
簡介
在我的前一篇小文中express-session小書提到了express-session可以更換會話儲存.
那么這篇文章我們就來講講express在進行會話管理的時候如何將會話數據保存在外部數據庫中,本文中我們使用mongodb用作會話儲存數據庫.
本文中使用的模塊以及版本號一覽:
模塊名稱 | 版本號 |
---|---|
express | 4.16.4 |
mongodb | 3.1.8 |
express-session | 1.15.6 |
connect-mongo | 2.0.3 |
支持Express5
支持所有版本的Connect
支持Mongoose>=4.1.2+
支持原生Mongodb驅動>=2.0.36
支持Node.js 4 6 8 10
支持Mongodb>=3.0
事前分析由于mongodb客戶端和服務器可以是多對多的關系,故有如下組合.
一個客戶端連接多個服務器
多個客戶端連接一個服務器
多個客戶端連接多個服務器
一個客戶端連接一個服務器
本文主要講解一個客戶端連接一個服務器.
這種情況下,一般服務器監聽一個端口,而我們希望可以共享同一個mongodb驅動的實例.
但是在一般情況下,我們的mongodb數據庫不可能只用于會話管理任務,所以本文復用同一個連接(端口).
只要復用同一個連接可以完成,那么使用多帶帶的驅動實例來用作會話管理也就不在話下了.
起步首先我們引入所有的模塊:
const Express = require("express")(), MongoClient = require("mongodb").MongoClient, ExpressSession = require("express-session"), MongoStore= require("connect-mongo")(ExpressSession);
看起來connect-mongo需要將express-session包裝一下,這步是固定的.
接下來我們定義幾個常量用于連接數據庫:
const UrlOfDb = "mongodb://localhost:27017", NameOfDb = "demo", Client = new MongoClient(UrlOfDb);// 創建mongodb客戶端
客戶端連接數據庫:
Client.connect((error) => { if (error) { throw error; } });
使用一個數據表,并且查詢幾條數據:
const DataBase = Client.db(NameOfDb), Collection = DataBase.collection("sessions"); Collection.find({}).toArray((error, result) => { if (error) { throw error; } for (const element of result) { console.log(element); } });
到目前為止我們沒有進行session管理,你可以替換本例中的數據表名稱用于測試一下運行是否正常.
完整代碼:
const Express = require("express")(), MongoClient = require("mongodb").MongoClient,// 獲取數據庫驅動 ExpressSession = require("express-session"),// 獲取session中間件 MongoStore= require("connect-mongo")(ExpressSession);// 獲取session儲存插件 const UrlOfDb = "mongodb://localhost:27017", NameOfDb = "demo", Client = new MongoClient(UrlOfDb);// 創建客戶端 Client.connect((error) => { if (error) { throw error; } const DataBase = Client.db(NameOfDb),// 獲取數據庫 Collection = DataBase.collection("sessions"); // 獲取數據表 // 查詢數據表 Collection.find({}).toArray((error, result) => { if (error) { throw error; } for (const element of result) { console.log(element); } }); });
現在我們來使用express-session中間件,并且替換掉默認的儲存:
// +++++ const DataBase = Client.db(NameOfDb),// 獲取數據庫 Collection = DataBase.collection("sessions"),// 獲取數據表 MongoStoreInstance = new MongoStore({ // 創建一個儲存實例,傳入db參數對于的數據庫對象 db:DataBase }); // 使用中間件 Express.use(ExpressSession({ secret: "hello mongo",// cookie簽名 cookie: {maxAge: 1800000}, rolling:true, saveUninitialized:true, resave: false, store:MongoStoreInstance // 替換掉默認的儲存 })); // +++++++
注意:connect-mongo會在該database下創建一個sessions的數據表(沒有這個數據表的情況下).
添加一個路由用于完成簡單的驗證,用于測試是否正常工作:
Express.get("/",(request,response)=>{ if(request.session.name){ response.send(`歡迎回來${request.session.name}`); return ; } // 使用查詢字符串當作保存的信息 request.session.name = request.query.name; request.session.pwd = request.query.pwd; response.send(`歡迎登錄${request.session.name}`); }); // 啟動服務器 Express.listen(8888, function () { console.log("server is listening 8888 port!"); });
完整代碼:
const Express = require("express")(), MongoClient = require("mongodb").MongoClient, ExpressSession = require("express-session"), MongoStore= require("connect-mongo")(ExpressSession); const UrlOfDb = "mongodb://localhost:27017", NameOfDb = "demo", Client = new MongoClient(UrlOfDb); function destroyDb(Client) { return destroyDb = function () { const info = "Client has been closed!"; Client.close(); Client = null; console.log(info); return info; } } Client.connect((error) => { if (error) { throw error; } const DataBase = Client.db(NameOfDb), Collection = DataBase.collection("sessions"), MongoStoreInstance = new MongoStore({ db:DataBase }); Express.use(ExpressSession({ secret: "hello mongo", cookie: {maxAge: 1800000}, rolling:true, saveUninitialized:true, resave: false, store:MongoStoreInstance })); // 使用閉包將關閉數據庫掛載到全局 destroyDb(Client); // 展示復用一個連接 Collection.find({}).toArray((error, result) => { if (error) { throw error; } for (const element of result) { console.log(element); } }); Express.get("/",(request,response)=>{ if(request.session.name){ response.send(`歡迎回來${request.session.name}`); return ; } request.session.name = request.query.name; request.session.pwd = request.query.pwd; response.send(`歡迎登錄${request.session.name}`); }); Express.get("/closedatabase", (request, respnose) => { respnose.send(destroyDb()); }); Express.listen(8888, function () { console.log("server is listening 8888 port!"); }); });
注意:我沒有刪除數據庫表的常規輸出,在這個例子啟動的時候,你會發現他們共用了同一個連接,啟動的時候會先輸出數據表中的內容.
測試在瀏覽器中輸入如下內容:
http://localhost:8888/?name=ascll&pwd=123456
瀏覽器輸出:
歡迎登錄ascll
直接再次訪問該頁面:
http://localhost:8888/
瀏覽器輸出:
歡迎回來ascll
此時在數據庫中手動查詢后,或者重啟本項目,你會在控制臺中發現上次留下的session記錄:
{ _id: "qbP36wE0nJkvtyNqx_6Amoesjjcsr-sD", expires: 2018-12-14T08:27:19.809Z, session: "{"cookie":{"originalMaxAge":1800000,"expires":"2018-12-14T08:20:21.519Z","httpOnly":true,"path":"/"},"name":"ascll","pwd":"123456"}" }使用總結
引入connect-mongo和express-session然后調用connect-mongo將express-sessino傳入
獲取上一步返回的類,然后使用express-session中間件的時候對于store選傳入這個類的實例對象
api 創建Express 4.x, 5.0 and Connect 3.x:
const session = require("express-session"); const MongoStore = require("connect-mongo")(session); app.use(session({ secret: "foo", store: new MongoStore(options) }));
Express 2.x, 3.x and Connect 1.x, 2.x:
const MongoStore = require("connect-mongo")(express); app.use(express.session({ secret: "foo", store: new MongoStore(options) }));連接到MongoDb 使用mongoose
const mongoose = require("mongoose"); // 基本使用 mongoose.connect(connectionOptions); app.use(session({ store: new MongoStore({ mongooseConnection: mongoose.connection }) })); // 建議使用方式,這樣可以復用連接 const connection = mongoose.createConnection(connectionOptions); app.use(session({ store: new MongoStore({ mongooseConnection: connection }) }));使用Mongo原生Node驅動
這種情況下你需要將一個mongodb驅動的一個數據庫實例傳遞給connect-mongo.如果數據庫沒有打開connect-mongo會自動幫你連接.
/* 這里有很多種方式來獲取一個數據庫實例,具體可以參考官網文檔. */ app.use(session({ store: new MongoStore({ db: dbInstance }) // 別忘了MongoStore是connect-mongo傳入express-session后返回的一個函數 })); // 或者也可以使用Promise版本 app.use(session({ store: new MongoStore({ dbPromise: dbInstancePromise }) }));通過連接字符串創建一個連接
// Basic usage app.use(session({ store: new MongoStore({ url: "mongodb://localhost/test-app" }) })); // Advanced usage app.use(session({ store: new MongoStore({ url: "mongodb://user12345:foobar@localhost/test-app?authSource=admins&w=1", mongoOptions: advancedOptions // See below for details }) }));事件
一個MongoStore實例有如下的事件:
事件名稱 | 描述 | 回調參數 |
---|---|---|
create | session創建后觸發 | sessionId |
touch | session被獲取但是未修改 | sessionId |
update | session被更新 | sessionId |
set | session創建后或者更新后(為了兼容) | sessionId |
destroy | session被銷毀后 | sessionId |
使用我們之前的例子中添加如下的代碼:
// +++ MongoStoreInstance.on("create",(sessionId)=>{ console.log("create",sessionId); }); MongoStoreInstance.on("touch",(sessionId)=>{ console.log("create", sessionId); }); MongoStoreInstance.on("update",(sessionId)=>{ console.log("update", sessionId); }); MongoStoreInstance.on("set",(sessionId)=>{ console.log("set", sessionId); }); MongoStoreInstance.on("destroy",(sessionId)=>{ console.log("destroy", sessionId); }); // +++
清空cookie后再次運行服務器,多執行幾個操作你就可以看到session的創建以及修改等操作.
session過期處理 基本處理方式connect-mongo只會使用配置了過期時間的cookie,如果沒有設置則會創建一個新的cookie并且使用tll選項來指定過期時間:
app.use(session({ store: new MongoStore({ url: "mongodb://localhost/test-app", ttl: 14 * 24 * 60 * 60 // 默認過期時間為14天 }) }));
注意:用戶的每次訪問都會刷新過期時間.
刪除過期session默認情況下connect-mongo使用MongoDB"s TTL collection特性(2.2+)用于自動的移出過期的session.但是你可以修改這種行為.
connect-mongo會在開始的時候創建一個TTl索引,前提是你的Mongo db版本在(2.2+)且有權限執行這一操作.
app.use(session({ store: new MongoStore({ url: "mongodb://localhost/test-app", autoRemove: "native" // Default }) }));
注意:這種默認的行為不適用于高并發的情況,這種情況下你需要禁用默認模式,然后自行定義TTl索引.
使用兼容模式如果你使用了Mongodb的老版本或者不希望創建TTL索引,你可以指定一個間隔時間讓connect-mongo來刪除這些過期的session.
app.use(session({ store: new MongoStore({ url: "mongodb://localhost/test-app", autoRemove: "interval", autoRemoveInterval: 10 // 單位分鐘 }) }));禁用過期session刪除
app.use(session({ store: new MongoStore({ url: "mongodb://localhost/test-app", autoRemove: "disabled" }) }));session懶更新
如果你使用的express-session版本>=1.10,然后不希望用戶每次瀏覽頁面的時候或刷新頁面的時候都要重新保存,你可以限制一段時間內更新session.
app.use(express.session({ secret: "keyboard cat", saveUninitialized: false, // 如果不保存則不會創建session resave: false, // 如果未修改則不會保存 store: new MongoStore({ url: "mongodb://localhost/test-app", touchAfter: 24 * 3600 // 指定觸發間隔時間 單位秒 }) }));
通過這樣設置session只會在24小時內觸發1次無論用戶瀏覽多少次頁面或者刷新多少次.修改session除外.
其他選項collection 指定緩存數據表的名字默認sessions
fallbackMemory 回退處理默認使用MemoryStore進行存儲
stringify 默認是true,如果為true則序列化和反序列化使用原生的JSON.xxx處理.
serialize 自定義序列化函數
unserialize 自定義反序列化函數
transformId 將sessionId轉為你想要的任何鍵然后進行儲存
暗坑也不算是暗坑吧,一用有兩點:
Mongodb客戶端正常關閉后connect-mongo會報錯,雖然會被Express攔截但是這個模塊沒有提供error事件.
Express中間件必須同步掛載?在我的例子中嘗試異步加載express-session中間件,但是失敗了中間件沒有效果.
connect-mongo模塊npm地址https://www.npmjs.com/package...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100056.html
摘要:簡介在我的前一篇小文中小書提到了可以更換會話儲存那么這篇文章我們就來講講在進行會話管理的時候如何將會話數據保存在外部數據庫中本文中我們使用用作會話儲存數據庫本文中使用的模塊以及版本號一覽模塊名稱版本號特性支持支持所有版本的支持支持 簡介 在我的前一篇小文中express-session小書提到了express-session可以更換會話儲存. 那么這篇文章我們就來講講express在進...
摘要:安裝安裝及其客戶端命令行工具查看版本啟動創建目錄,用于數據和日志存儲啟動注首次啟動可能會花費大概時間可以使用下面的命令來檢查是否啟動成功注默認監聽端口添加用戶登錄本地服務創建用戶退出安裝模塊實現小程序的會話功能 1.安裝MongoDB #安裝 MongoDB及其客戶端命令行工具 yum install mongodb-server mongodb -y #查看版本 mongod --v...
摘要:當會話過期或被放棄后,服務器將終止該會話。原來中間件生成的是一個對象,里面包含了信息。這個有一個過期時間,比如,上面代碼中設置的是小時。也就是說,小時后,這個在瀏覽器中會自動消失。 前言 在上一篇中node中的cookie,對cookie進行了相關介紹,本篇將繼續前行,對session進行說明。 session是什么 session不就是會話嘛,那什么是會話呢?會話是一個比連接粒度更大...
摘要:當會話過期或被放棄后,服務器將終止該會話。原來中間件生成的是一個對象,里面包含了信息。這個有一個過期時間,比如,上面代碼中設置的是小時。也就是說,小時后,這個在瀏覽器中會自動消失。 前言 在上一篇中node中的cookie,對cookie進行了相關介紹,本篇將繼續前行,對session進行說明。 session是什么 session不就是會話嘛,那什么是會話呢?會話是一個比連接粒度更大...
閱讀 2419·2021-10-11 10:57
閱讀 1274·2021-10-09 09:59
閱讀 1986·2019-08-30 15:53
閱讀 3206·2019-08-30 15:53
閱讀 1001·2019-08-30 15:45
閱讀 727·2019-08-30 15:44
閱讀 3433·2019-08-30 14:24
閱讀 946·2019-08-30 14:21