使用過幾種Web App開發語言和框架,都會接觸到Session的概念。即便是一個簡單站點訪問計數的功能,也經常使用Session來實現的。其餘經常使用的領域還有購物車,登陸用戶等。可是,對Session一直是隻知其一;不知其二,知其然而不知其因此然。php
在認真的研究了HTTP協議,以及nodejs開發棧的express和express-session後,我終於比較有把握深刻淺出的說清楚Session了,也算是知足了多年來開發過程當中,經常浮現的對Session的好奇心吧。node
本文使用nodejs v9.5.0做爲技術驗證工具。閱讀本文前須要瞭解基礎的HTTP知識和Cookie知識。詳細須要參考rfc6265,或者閱讀《HTTP小書》的最後一章。mysql
用戶在網站的一組相互關聯的的請求和響應,就是一次會話。簡而言之是這樣的:redis
好比一個最簡單的nodejs HTTP程序:算法
var http = require('http') http.createServer(function(req,res){ res.end('hello') }).listen(3000)
每一個請求都會進入到此處理函數:function(req,res){res.end('hello') }
,在此函數內得到請求,處理響應,完成後發給客戶端,就是一次訪問。經過瀏覽器的developer tools,能夠看到這次會話的請求內容和響應內容。sql
以站點計數應用爲案例來講明的話,就是這些來自於一樣訪問者的屢次訪問,均可以得到當前站點的訪問計數。mongodb
咱們從一個案例開始引入會話的概念。當咱們須要訪問站點計數一類的功能時,咱們但願用戶訪問此站點時:chrome
此種狀況下,咱們須要有一個地方存儲當前計數,這樣才能在同一個客戶在此訪問時,能夠取出當前計數,加一後返回給客戶。固然也所以須要識別此用戶(瀏覽器),爲每一個用戶單獨計數。就是說,不一樣的用戶訪問時,須要去取對應用戶的當前計數。數據庫
識別客戶的問題,經常使用的方法就是使用Cookie。Cookie是HTTP協議的一部分。HTTP能夠經過頭字段Set-Cookie爲來訪客戶作一個標記,這個標記經常就是一個ID,下一次訪問此站點時,HTTP會經過Cookie頭字段,發送此ID到站點,由此站點知道此客戶的身份和這個身份關聯的狀態信息,好比當前訪問計數,或者此身份當前的購物車的內容等等。express
識別了客戶後,就能夠在Web服務器內,爲此客戶創建它的獨特的狀態信息。
基於nodejs HTTP模塊,咱們實現一個極爲簡單的Session服務。只是爲了展現概念,而不是爲了實用的目的。此服務能夠實現一個共享於同一站點的屢次訪問的req.session變量,此變量爲一個對象,能夠在此變量內寫入新的成員,或者修改現存的成員變量的值,每次訪問後會保存req.session,以便下次訪問能夠獲得當前的值:
var http = require('http') var sessionkey = "sessionkey3" http.createServer(function(req,res){ if (req.url =="/"){ session(req,res) req.session.count = (req.session.count+1) || 1 res.end('hi'+req.session.count) }else res.end('') }).listen(3000) console.log('listen on 3000') function session(req,res){ if (req.session) return var answer ,id if(isSessionOk(req)){ id = getCookie(req) answer = getSessionById(id) }else{ answer= {} id = createSession(answer) setCookie(res,id) } req.session = answer res.on('finish', function() { saveSession(id,req.session) }); } function hasCookie(req){ return (getCookie(req)!='') } function getCookie(req){ try{ var c = req.headers['cookie'] var arr = c.split(';') for (var i = 0; i < arr.length; i++) { var kv = arr[i] var a = kv.split('=') if (a[0].trim() == sessionkey) return a[1] } }catch(error){ return '' } return '' } function setCookie(res,id){ res.setHeader("set-cookie",sessionkey +"="+id) } var sessions = {} var sid = 0 function getSessionById(sid){ return sessions[sid] } function getSessionByReq(req){ var sid = getCookie(req) return sessions[sid] } function createSession(session){ sessions[sid++,session] return sid } function saveSession(sid,session){ sessions[sid] = session } function isSessionOk(req){ return hasCookie(req) && getSessionByReq(req) !== undefined }
程序代碼比較簡單,讀者能夠保持它到index.js,而後執行此程序,驗證概念:
node index.js
而後,啓動chrome,訪問站點localhost:3000
,而後屢次刷新,你能夠看到每次刷新,返回的訪問次數逐步累加。在打開另外一個瀏覽器,好比safari,在此訪問此站點,你會發現返回的訪問計數從1開始,另外計數。由於是兩個不一樣的瀏覽器內器,這就保證的它們是不一樣的訪問客戶,在站點內的代碼,會區別二者,分別記錄它們的狀態信息。
代碼使用了HTTP Cookie,基本算法很簡單:
可能你們看到sessionkey這個變量,感受有些莫名其妙。緣由是每次cookie發送,一樣的站點可能有多個框架須要使用此cookie頭字段,好比php,aspx,jsp等都是須要使用了,爲了好像不要衝突,你們各自使用cookie頭字段內各自的key/value對便可。好比php的key默認是phpsessid,express-session默認的是connect.sid。
此代碼演示了最基礎的Session的概念,可是遠遠不是一個可用的模塊,想要真實世界中使用的Session模塊,能夠考慮express-session。
實現一個真正能夠的會話,還須要考慮不少問題:
更多的考量,能夠去經過閱讀express-session來得到。本文閱讀完畢,自己就是能夠成爲閱讀express-session的基礎材料的。