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 的实现流程。

 

文章中若是有写的不恰当的地方,欢迎你们交流指正。

相关文章
相关标签/搜索