Koa系列-Session:koa-session
2019-05-08
#koa
字数统计:33k 字
阅读时长 ≈ 30 分钟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 module .exports = function (opts, app ) { if (opts && typeof opts.use === 'function' ) { [ app, opts ] = [ opts, app ]; } if (!app || typeof app.use !== 'function' ) { throw new TypeError ('app instance required: `session(opts, app)`' ); } opts = formatOpts (opts); extendContext (app.context , opts); return async function session (ctx, next ) { const sess = ctx[CONTEXT_SESSION ]; if (sess.store ) await sess.initFromExternal (); try { await next (); } catch (err) { throw err; } finally { if (opts.autoCommit ) { await sess.commit (); } } }; };
当设置了 ctx.session.views = 1
时,
在类 ContextSession
中即 set
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 set (val ) { if (val === null ) { this .session = false ; return ; } if (typeof val === 'object' ) { this .create (val, this .externalKey ); return ; } throw new Error ('this.session can only be set as null or an object.' ); }
类 ContextSession
,方法 get
1 2 3 4 5 6 7 8 9 10 get ( ) { const session = this .session ; if (session) return session; if (session === false ) return null ; if (!this .store ) this .initFromCookie (); return this .session ; }
类 ContextSession
,方法 create
1 2 3 4 5 6 7 create (val, externalKey ) { debug ('create session with val: %j externalKey: %s' , val, externalKey); if (this .store ) this .externalKey = externalKey || this .opts .genid (); this .session = new Session (this , val); }
类 Session
会话状态模型的作用是操作 会话状态值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Session { constructor (sessionContext, obj ) { this ._sessCtx = sessionContext; this ._ctx = sessionContext.ctx ; if (!obj) { this .isNew = true ; } else { for (const k in obj) { if (k === '_maxAge' ) this ._ctx .sessionOptions .maxAge = obj._maxAge ; else if (k === '_session' ) this ._ctx .sessionOptions .maxAge = 'session' ; else this [k] = obj[k]; } } } ... ... }
类 ContextSession
,方法 initFromCookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 initFromCookie ( ) { debug ('init from cookie' ); const ctx = this .ctx ; const opts = this .opts ; const cookie = ctx.cookies .get (opts.key , opts); if (!cookie) { this .create (); return ; } let json; debug ('parse %s' , cookie); try { json = opts.decode (cookie); } catch (err) { debug ('decode %j error: %s' , cookie, err); if (!(err instanceof SyntaxError )) { ctx.cookies .set (opts.key , '' , opts); err.headers = { 'set-cookie' : ctx.response .get ('set-cookie' ), }; throw err; } this .create (); return ; } debug ('parsed %j' , json); if (!this .valid (json)) { this .create (); return ; } this .create (json); this .prevHash = util.hash (this .session .toJSON ()); }
还有三个比较重要的方法
类 ContextSession
,方法 commit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 async commit ( ) { const session = this .session ; const opts = this .opts ; const ctx = this .ctx ; if (undefined === session) return ; if (session === false ) { await this .remove (); return ; } const reason = this ._shouldSaveSession (); debug ('should save session: %s' , reason); if (!reason) return ; if (typeof opts.beforeSave === 'function' ) { debug ('before save' ); opts.beforeSave (ctx, session); } const changed = reason === 'changed' ; await this .save (changed); }
类 ContextSession
,方法 _shouldSaveSession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 _shouldSaveSession ( ) { const prevHash = this .prevHash ; const session = this .session ; if (session._requireSave ) return 'force' ; const json = session.toJSON (); if (!prevHash && !Object .keys (json).length ) return '' ; const changed = prevHash !== util.hash (json); if (changed) return 'changed' ; if (this .opts .rolling ) return 'rolling' ; if (this .opts .renew ) { const expire = session._expire ; const maxAge = session.maxAge ; if (expire && maxAge && expire - Date .now () < maxAge / 2 ) return 'renew' ; } return '' ; }
类 ContextSession
,方法 save
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 async save (changed ) { const opts = this .opts ; const key = opts.key ; const externalKey = this .externalKey ; let json = this .session .toJSON (); let maxAge = opts.maxAge ? opts.maxAge : ONE_DAY ; if (maxAge === 'session' ) { opts.maxAge = undefined ; json._session = true ; } else { json._expire = maxAge + Date .now (); json._maxAge = maxAge; } if (externalKey) { debug ('save %j to external key %s' , json, externalKey); if (typeof maxAge === 'number' ) { maxAge += 10000 ; } await this .store .set (externalKey, json, maxAge, { changed, rolling : opts.rolling , }); if (opts.externalKey ) { opts.externalKey .set (this .ctx , externalKey); } else { this .ctx .cookies .set (key, externalKey, opts); } return ; } debug ('save %j to cookie' , json); json = opts.encode (json); debug ('save %s' , json); this .ctx .cookies .set (key, json, opts); }
总结一下:
这是一个管理会话状态的一个 Koa
中间件,并且可以引入其他的存储方式,如 Redis
和 MongoDB
等。