为什么一个对象的原型会被遗忘?

Sea*_*dre 6 session serialization prototype node.js express

介绍

所以我正在用 node.js、express、express-session 和 sequelize.js 构建一个网站。用户登录后,将User创建Sequelize 模型的实例。在我的用户登录 ( /auth/login)路线中,我有:

var user = (await User.findAll(
        {where: {
            username: username
        }}))[0];
Run Code Online (Sandbox Code Playgroud)

我把它分配user给会话。

req.session.user = user;
Run Code Online (Sandbox Code Playgroud)

然后我可以通过简单地调用以下save方法来保留任何更改req.session.user

await req.session.user.save();
Run Code Online (Sandbox Code Playgroud)

事实上,如果我接下来添加这一行:

console.log(Object.getPrototypeOf(req.session.user));
Run Code Online (Sandbox Code Playgroud)

输出是[object SequelizeInstance:User]。到现在为止还挺好。

这是问题所在

在另一条路线 ( /users/myaccount/edit-bio) 中,我能够访问req.session.user. 也就是说,输出

console.log(req.session.user.username);
Run Code Online (Sandbox Code Playgroud)

seanletendre,正如预期的那样。但是现在当我打电话

await req.session.user.save();
Run Code Online (Sandbox Code Playgroud)

我得到的只是错误消息:

UnhandledPromiseRejectionWarning: TypeError: req.session.user.save is not a function
Run Code Online (Sandbox Code Playgroud)

“这很奇怪,”我想,“这不是同一个物体吗?” 为了进行调查,我添加了以下行:

console.log(Object.getPrototypeOf(req.session.user));
Run Code Online (Sandbox Code Playgroud)

就像我在登录路径中所做的那样。输出是什么?它是:[object Object]。所以似乎不知何故原型req.session.user被遗忘了。我不明白这怎么可能。

是否可以将原型重新分配给普通对象?

嫌疑人A

根据对我的问题的评论,我怀疑当会话管理器序列化req.session. 似乎,与我之前的想法不同,对于不同的请求,req.session它并没有指向完全相同的session对象。每次请求结束时,它都会序列化并存储req.session. 然后在收到带有 cookie 的新请求时,将其指定为同一会话的一部分,session从会话存储中获取该对象。

这是我的会话中间件的设置方式:

var session = require('express-session');
//
// yada, yada, yada
//
app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {secure: true}
}));
Run Code Online (Sandbox Code Playgroud)

所以令我惊讶的是,即使我使用的是默认 store MemoryStore,我的会话仍然是序列化的。

我现在的问题变成:如何在使用时防止会话存储上的对象序列化MemoryStore

Sim*_*gro 3

express-session中,该方法save()由对象公开到对象(文档session)中,例如:request

req.session.save(callback)
Run Code Online (Sandbox Code Playgroud)

你的代码req.session.user.save()是错误的,正确的方法是req.session.save(),diff.:

req.session.user.save();
-----------^^^^^
req.session.save()
Run Code Online (Sandbox Code Playgroud)

该方法save()不是 a Promise,您必须传递回调以等待保存结果:

req.session.user = user;
req.session.save(function(err) {
  if( err ){
    // session not saved
  } else {
    // session saved
  }
})
Run Code Online (Sandbox Code Playgroud)

您可以将其转换为 a Promise(并等待它),如下所示:

const saveSession = (req) => {
  return new Promise((resolve, reject) => {
    req.session.save(function(err) {
      if( err ){
        reject(err);
      } else {
        resolve(true);
      }
    });
  });
};

req.session.user = user;
await saveSession(req);
Run Code Online (Sandbox Code Playgroud)

save()如果会话数据已更改,则会在 HTTP 响应结束时自动调用该方法。因此,通常不需要调用此方法。

更新

我调用 req.session.user.save() 因为我想将 Sequelize Model 实例保存到我的数据库中。

express-session 用于JSON.stringify()将会话序列化为字符串并存储到会话存储中(更多信息)。JSON.stringify()不理解函数,只存储属性。因此,你的save()功能就丧失了。