Egg: io.controller和io.middleware的context都无法获取到session

Created on 12 Sep 2017  ·  30Comments  ·  Source: eggjs/egg

  • Node Version: 7.10.0
  • Egg Version: 1.7.0
  • Plugin Name: egg-socket.io
  • Plugin Version: 2.1.0
  • Platform: Windows
  • Mini Showcase Repository:


io.controller和io.middleware都无法获取到session,但app.controller中可以获取,请问是什么原因?

egg-socket.io

Most helpful comment

查到原因了:

被 socket.io 接管的请求,都不会进去到 http.createServer(this.callback()); 这个逻辑中去,也就是所有 koa 的中间件都无法执行。

同时,https://github.com/koajs/session/blob/master/index.js#L38 koa-session 是在中间件内部,来处理 session-store 逻辑的。

All 30 comments

socket.io 和 http 不是一个模型来着

那我需要怎么做才可以拿到session呢?

请提供复现代码

module.exports = app => {
    return function* (next) {
        // this.socket.emit('res', 'connected!');

        console.log(this.socket.handshake);

        console.log(app.context.sessionOptions);

        console.log(this.session.temp);

        console.log('connected!');

        yield* next;

        console.log('disconnection!');
    };
};

基本什么都还没开始处理,就是想先通过web api授权,并拿到session id,然后在socket.io这边可以直接使用session的内容

这样看不出什么来。请按照模板提供 Mini Showcase Repository:

这样,我就想确认,在socke.io的plugin里面,对应的this对象,是不是能拿到session,如果不能,我就自己想办法包装一个,我想这个不需要我提供代码吧?

@atian25 也就是说如果session是基础cookie的,那么在controller和middleware 中是无法改变session的是吧。

@liujinyang1994 可以获取的看下我上面发的例子

@ngot 当session使用redis来实现的时候,是取不到的

@liujinyang1994 我也发现了这个问题,的确拿不到,但是可以手动从cookie中取一下。

const session = await app.redis.get(this.cookies.get('EGG_SESS', { encrypt: true }));
console.log(JSON.parse(session));  

@Evilcome 我们要不要再开个issue,他们好像没注意到这个问题。

写一个复现的repo,给我调试下

@ngot 我写了一个,尝试注释开 plugin 中的 sessionRedis 设置。

https://github.com/Evilcome/egg-socket-redis-session-demo

测试步骤:

  1. 访问 http://127.0.0.1:7001/ 保存 session
  2. 访问 http://127.0.0.1:7001/public/socket-client.html 来测试链接。

我在这里做了更多说明
// /app/io/middleware/auth.js

'use strict';

module.exports = app => {
  return function* (next) {

    // NOTE: 如果用了 session-redis, 这里就拿不到 session 了,
    // 如果尝试把 plugin 中 sessionRedis 注释掉,既可以访问到。
    // ISSUES: https://github.com/eggjs/egg/issues/1416
    if (!this.session.user) {
      return this.socket.emit('forbidden');
    }

    yield* next;
  };
};

@Evilcome 赞,我还想晚上写一个来着。

查到原因了:

被 socket.io 接管的请求,都不会进去到 http.createServer(this.callback()); 这个逻辑中去,也就是所有 koa 的中间件都无法执行。

同时,https://github.com/koajs/session/blob/master/index.js#L38 koa-session 是在中间件内部,来处理 session-store 逻辑的。

koa的中间件,目前全部基于 http 模型设计,并且 socket.io 的请求也无法经过 koa 中间件。基于 socket 路径的请求,简单的处理办法是,单独编写中间件。

@atian25 @popomore 有没有好的思路?

cc @fengmk2

感觉要重新设计,不同入口进来,创建 ctx,走相同的流程。只是 ctx 会有差异。

一个走的是http 一个走的是socketio 两者挂载的父级对象是分开的 完全是两个分支 所以你拿不到
但是你可以通过http那部分模型把userid作为变量输出到前端 前端js通过客户端socket api再把userid发送到后端socketio服务来区分用户

这种就属于hack方法了,还是等官方把这个问题修复吧。

@killagu 这个跟你思考的也有点像

简单的思路

不同的协议适配requestresponse的抽象, 不用改造现有的中间件, 可以使不同的协议都走一套中间件。

附上一些伪代码

protocolConnection.on('req', req => handleProtocolRequest(req))

// 处理某种协议的请求
function handleProtocolRequest(protocolRequest) {
  // 将某种协议的请求封装成req, res对象
  // 适应koa的`request`和`response`抽象
  const { req, res } = protocolReqImpl(req);
  // 创建ctx
  const ctx = app.createContext(req, res);
  // 处理请求, 使用通用的中间件
  app.handleRequest(ctx, app.middleware);
}

cc @ngot

两者不应该共享中间件,模型不一样。

socket.io 可以实现一个 session 的中间件,保证两边的通用。其他的还是独立走中间件吧

现在有啥简单的workaround么?按照@Evilcome 提到的从cookie拿EGG_SESS,再从redis拿session,这种方法可以拿到session,但是不好存下去。
要么就不用redis来存session,然后把本来要存到session的东西,直接存到redis,用cookie方式来存session的id?

这个问题现在解决了吗?

这个问题还在解决吗

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Azard picture Azard  ·  3Comments

Webjiacheng picture Webjiacheng  ·  3Comments

skyyangpeng picture skyyangpeng  ·  3Comments

yuu2lee4 picture yuu2lee4  ·  3Comments

Quekie picture Quekie  ·  3Comments