zoukankan      html  css  js  c++  java
  • Koa-session源码学习——程序执行流程

    最近在研究 koa2 ,感觉 koa-session 插件用起来特别顺手,再加上自己一直对 cookie、session 感兴趣,索性研究起了 koa-session 源码,过程有点小艰辛,不过研究过后,感觉还是收货满满,很开心。现将研究成果分享给大家,希望对大家有帮助。

    首先来看一个简单的例子,实现的是当浏览器访问 localhost:3000,进行 ctx.session.views = 2,创建 session 。

    示例代码:

    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 里具体经过了哪些处理流程。

    OK ,下面就要正式研究 koa-session 的源码了,打开源码目录,你会发现目录结构很简单,主要的 4 个 js 文件如下:

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

     首先,当然是从 index.js 说起:

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

    对应示例代码里:

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

    我们来看 session(CONFIG, app),也就是 module.exports 导出的模块,这里也就是 koa-session 源码的入口。

    接着继续看 index.js:

    opts = formatOpts(opts);             //对opts进行处理,设置默认值等,该方法里的opts.store是session存储于数据库的情况。
    extendContext(app.context, opts);    //index.js里非常重要的一个方法,对app.context也就是ctx,进行扩展,新增了属性session

    继续i ndex.js ,这段代码是 index.js 的重点, await next() ,代表程序的执行从 index.js 转移到了 我们开头的示例代码,也就是 app.use((ctx)  =>  {}) 里的代码,执行完毕后,又回到了 finally 里的 await sess.commit() 。我们可以看到 sess.commit() 应该是一个提交操作。

    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 在这里初始化,我们将t his.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 的实现流程。

    文章中如果有写的不恰当的地方,欢迎大家交流指正。

  • 相关阅读:
    2020.8.20收获
    2020.8.19
    2020.8.21收获
    2020.8.24收获
    UIScrollView滑动动作结束的侦听函数
    iphone 自定义UISwitch
    总结SQLite不支持的SQL语法有哪些
    去除nsstring中的空格
    ObjectiveC中判断字符串是否包含其他字符串
    设置IPHONE顶部的状态栏的样式
  • 原文地址:https://www.cnblogs.com/Fcode-/p/13068186.html
Copyright © 2011-2022 走看看