在yog2框架中自建模块,以实现socket.io与express共享session中间件

czmdzx 6年前

来自: http://segmentfault.com/a/1190000004392232

导引

最近遇到了一个需要在yog2框架中增加websocket服务的案子,此文主要讲在案子过程中碰到的一些问题和解决方案

由于此文和yog2这个框架高度相关,如需了解yog2请移步 https://github.com/fex-team/yog2

yog2 是一个专注于 Node.js UI 中间层的应用框架。它基于 express 和 fis 开发,在享受 express 的灵活扩展能力和 fis 强大的前端工程化能力的同时,引入了自动路由、app 拆分以及后端服务管理模块来保证UI中间层的快速开发与稳定可靠。

我的做法

需要在yog框架中增加websocket服务,我的做法是在yog的入口文件 yog/app.js 中将socket.io服务器绑定在原有express的server上(也可以绑定在新端口如8081上,看你喜欢),然后在 别处[1] 进行业务逻辑和路由

[1] :别处的意思是将业务代码最好放在别的文件夹里,这个根据你们的项目自己决定

我遇到的问题

socket.io服务器的中间件(主要是解析/验证session的那个)需要重新去写,当前项目使用配置过的 express-session 中间件完成对session的解析,这完全是重复造轮子

对此问题我的想法

让socket.io服务器共享express的中间件!

此文答案一 启发,可以将 express-session 模块定义的中间件提出来给socket.io用。

但是问题来了,yog2已经对中间件的摆放位置进行了一下处理,主要在 yog/conf/plugins/http.js 内实现(见下文结构树,或见 官方文档 ),如何将已定义的中间件提出来呢?

解决方案

新建自己的session解析中间件模块 mysession ,在yog2的结构树中的摆放位置如下

├── yog      ├── app                          ├── conf                      │    ├── plugins                      │    │    ├── http.js      │    │    ├── dispatcher.js      │    │    ├── log.js      │    │    ├── view.js      │    │    ├── mysession.js    - mysession模块的配置文件      ├── plugins                          │    ├── mysession                  │    │    ├── index.js        - mysession模块文件      ├── static      ├── views      └── app.js

模块文件放置入 yog/plugins/mysession/index.js ,详见 用户插件相关文档

module.exports.mysession = function(app, conf){      return function(){          app.use(conf);      };  };

那么坑来了, 官方文档 里说

app为yog.app对象,即Express的app

conf为插件的配置项

module.exports后的属性名就是插件的真实名称

</div>

app 参数解释很清楚,那么 conf 参数是啥呢?!

通过安装 中间件文档末尾 中提到的session模块

yog2 plugin install session

我发现,这个 conf 参数将从 yog/conf/plugins/你的模块名.js 中export的变量中获取。

一个字,坑!

于是照葫芦画瓢,将 mysession 模块的配置文件放置入 yog/conf/plugins/mysession.js

(你可以用你自己的session解析模块,比如 connect-memcached 也可以,此处用 session-file-store )

// example using session-file-store to store session  var maxAge = 30 * 24 * 3600 * 1000;  var ttl = maxAge / 1000;  var sessionFileStoreGenerator = require('session-file-store');  var SessionFileStore = sessionFileStoreGenerator(session);  sessionStore = new SessionFileStore({ ttl: ttl });  var conf = session({      secret: 'mysession-key',      store: sessionStore,      resave: false,      saveUninitialized: true,      cookie: {          maxAge: maxAge      }  });  module.exports.mysession = conf;

最后在 yog/conf/plugins/http.js 中加入这个自定义中间件

// ...  module.exports.http = {       middleware: [           // ...           'views',           'methodOverride',           'mysession', //加入我的新中间件           'dispatcher',           'notFound',           'error',           // ...       ]  };  // ...

至此,已完成将session解析中间件提取出来,剩下的就是如 此文答案一 中提到的,使用如下代码共用这个中间件

sio.use(function(socket, next) {      sessionMiddleware(socket.request, socket.request.res, next);  });

具体实现代码如下:编辑入口文件 yog/app.js

var yog = require('yog2-kernel');    // 此处载入这个共享的session解析中间件  var sessionMiddleware = require('./conf/plugins/mysession.js').mysession;    var app = yog.bootstrap({      rootPath: __dirname  }, function(){            var server = yog.server = app.listen(process.env.PORT || 8080);            // 以下是为socket.io新加入的代码            // 此处绑定在express的server实例上,你也可以绑定一个新端口      var sio = require('socket.io')(server);       sio.use(function(socket, next) {          sessionMiddleware(socket.request, socket.request.res, next);      });      require('../你的socket.io业务逻辑处理与路由代码文件')(sio);        // 以上是为socket.io新加入的代码        });

结语

当然,我对自己的这些解决方案也不是完美认同,比如:

  • 编辑入口文件某些情况下并不是best practice,也可以在别处启动socket服务。

  • 通过改变yog2的已有架构来使其更好的支持代码重用

  • 在入口文件中引用模块也是一个很糟糕的做法,有没有更优雅的做法呢?

对于在yog2内使用socket的问题、共享中间件的问题等,一定还有更好的解决方案,所以在此抛砖引玉一下,大家都会怎么做?

更多兴趣阅读

在探索的过程中,读到一篇好答案,关于express的session解析模块的sign机制。

感兴趣的请移步 Why is it insecure to store the session ID in a cookie directly?

</div>