Koa-session源碼學習——程序執行流程

最近在研究koa2,感受koa-session插件用起來特別順手,再加上本身一直對cookie、session感興趣,索性研究起了koa-session源碼,過程有點小艱辛,不過研究事後,感受仍是收貨滿滿,很開心。現將研究成果分享給你們,但願對你們有幫助。數據庫

 

首先來看一個簡單的例子,實現的是當瀏覽器訪問localhost:3000,進行ctx.session.views = 2,建立session。瀏覽器

示例代碼:cookie

const session = require('koa-session');
const Koa = require('koa');
const app = new Koa();

app.keys = ['some secret hurr'];        //若CONFIG裏,signed爲true,則須要app.keys生成簽名
const CONFIG = {
  key: 'koa:sess', //到源碼階段就會理解(session以cookie形式存儲),這裏的key至關於ctx.cookies.set(key,val)裏的key,能夠設置爲任意值,默認爲koa:sess
  maxAge: 86400000,
  overwrite: true, /** (boolean) can overwrite or not (default true) */
  httpOnly: true, /** (boolean) httpOnly or not (default true) */
  signed: true, /** (boolean) signed or not (default true) */
  rolling: false, /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. default is false **/
};
app.use(session(CONFIG, app));

app.use((ctx) => {
  ctx.session.views = 2;
  ctx.body =' views' +'miews';
})

app.listen(3000);

 

咱們的目標是搞清楚 ctx.session.views = 2,這一句代碼,在koa-session裏具體通過了哪些處理流程。session

 

OK,下面就要正式研究koa-session的源碼了,打開源碼目錄,你會發現目錄結構很簡單,主要的4個js文件以下:app

|--index.js
|-- lib
   |-- context.js             
   |-- session.js
   |--utils.js

 首先,固然是從index.js提及:koa

module.exports = function(opts, app) {
}

對應示例代碼裏:async

const session = require('koa-session');
app.use(session(CONFIG, app));

咱們來看 session(CONFIG,app),也就是module.exports導出的模塊,這裏也就是koa-session源碼的入口。ide

 

接着繼續看index.js:ui

opts = formatOpts(opts);             //對opts進行處理,設置默認值等,該方法裏的opts.store是session存儲於數據庫的狀況。
extendContext(app.context, opts);    //index.js裏很是重要的一個方法,對app.context也就是ctx,進行擴展,新增了屬性session

 

繼續index.js,這段代碼是index.js的重點, await next(),表明程序的執行從index.js轉移到了 咱們開頭的示例代碼,也就是app.use((ctx) => {})裏的代碼,執行完畢後,又回到了 finally裏的 await sess.commit()。咱們能夠看到sess.commit()應該是一個提交操做。this

return async function sessions(ctx, next) {
    const sess = ctx[CONTEXT_SESSION];
    console.log(sess.session);
    if (sess.store) await sess.initFromExternal();
    try {
      await next();
      console.log(sess.session);
    } catch (err) {
      throw err;
    } finally {
      if (opts.autoCommit) {
        await sess.commit();
      }
    }
  };

 

咱們接着看index.js,結合示例代碼裏的 ctx.session.views = 2語句。首先,ctx.session.views裏的ctx.session觸發了 function extendContext(context, opts) {}裏,session屬性的getter:

 session: {
      get() {
        return this[CONTEXT_SESSION].get();
      },
     ....
}

 

返回的是ctx[CONTEXT_SESSION]的get方法的返回值,其中ctx[CONTEXT_SESSION]屬性,則是建立了一個新的類:

this[_CONTEXT_SESSION] = new ContextSession(this, opts);   //class ContextSession 位於./lib/context.js

 

咱們來看class ContextSession的get方法:(this.session 也就是index.js中return async function sessions(ctx, next) {} 裏的  sess.session)

get() {
    const session = this.session;
    // already retrieved
    if (session) return session;
    // unset
    if (session === false) return null;

    // create an empty session or init from cookie
    this.store ? this.create() : this.initFromCookie();
    return this.session;
  }

咱們先不考慮this.store,因而程序執行了 this.initFromCookie(),並將this.session返回給ctx.session, this.initFromCookie()也就是從cookie中初始session,initFromCookie()方法中調用了create()方法,建立session,咱們來看create()方法:

 

 create(val, externalKey) {
    debug('create session with val: %j externalKey: %s', val, externalKey);
    if (this.store) this.externalKey = externalKey || this.opts.genid && this.opts.genid(this.ctx);
    this.session = new Session(this, val);
  }

 

因爲咱們示例代碼中的 ctx.session.views是第一次執行,因此initFromCookie() {}方法裏的const cookie = ctx.cookies.get(opts.key, opts); 爲空,所以create裏的val參數也爲空,此時ContextSession 的get方法返回的this.session在這裏初始化,咱們將this.session打印出來,以下:

Session {_sessCtx: ContextSession, _ctx: Object, isNew: true}

 

this.session 也就是ctx.session.views=2中的「ctx.session」的返回值,咱們再來看一遍ctx.session屬性的get():

 session: {
      get() {
        return this[CONTEXT_SESSION].get();
      },
     ....
}

 

而ctx.session.views至關於給ctx.session又新增了一個views屬性,並賦值2,也就等於給 this.session新增了views屬性,因而this.session變成了:

Session {_sessCtx: ContextSession, _ctx: Object, views: 2,isNew: true}

 

而this.session也就是index.js 中的return async function sessions(ctx, next) {} 中的 sess.session,也就至關於sess.session變爲了:

 

Session {_sessCtx: ContextSession, _ctx: Object, views: 2,isNew: true}

 

這裏是整個koa-session源碼的核心部分,須要你細品。

 

而後,context.js中的async commit() {}方法裏,將this.session保存到了cookie中,到此也就實現了 ctx.session.views = 2 的實現流程。

 

文章中若是有寫的不恰當的地方,歡迎你們交流指正。

相關文章
相關標籤/搜索