【问题标题】:Express cookieSession and Mongoose: how can I make request.session.user be a Mongoose model?Express cookieSession 和 Mongoose:如何使 request.session.user 成为 Mongoose 模型?
【发布时间】:2013-09-01 23:25:59
【问题描述】:

创建新用户帐户后,我创建 newUser,这是一个 Mongoose 模型实例,如下所示:

_events: Object
errors: undefined
isNew: false
save: function () {
arguments: null
caller: null
_doc: Object
  name: 'Joe Smith'
...

对象的实际数据在 _doc 属性中,尽管 getter 和 setter 存在,因此您可以运行:

user.name = 'Jane Doe' 

这样就可以了。我跑:

request.session.user = newUser;

将用户保存到会话中。到目前为止,一切顺利。

但是,在随后的请求中,request.session.user 似乎只是 _doc 中的东西。例如:

name: 'Joe Smith'

这很好,但这意味着我不能运行例如 request.session.user.save() 来保存更改。

我可以简单地制作一些中间件来查找与数据关联的用户。但我想了解更多关于 Express 和 Mongoose 在这里所做的事情。

如何让 request.session.user 成为 Mongoose 模型?

更新:我目前的中间件破解:

// Use as a middleware on routes which need users
// TODO: only needed due to request.session.user being saved weirdly
var rehydrateUser = function(request, response, next) {
  if ( request.session.user ) {
    var hydrated = request.session.user.save
    if ( ! hydrated ) {
      console.log('Rehydrating user...');
      models.User.findOne({ somefield: request.session.user.somefield }, function (err, user) {
        if ( err ) {
          request.session.error = 'User not found in DB!';
          request.redirect('/');
        } else {
          request.session.user = user;          
          console.log('Rehydrated user! All repos already being monitored.');
          next();
        }
      })
    } else {
      next();
    }
  } else {
    console.log('No user in session')
    next();
  }
}

【问题讨论】:

    标签: node.js mongodb session express mongoose


    【解决方案1】:

    尽管取决于您的会话存储来保存整个用户对象存在复杂性问题,但应该注意,您可以使用init() 方法从普通 JS 对象初始化一个猫鼬对象(如在撰写本文时,v4.4.14)。

    req.user = new UserModel().init(req.session.user);
    req.user.name = 'James';
    req.user.save( ... );
    

    这样(它会尝试使用相同的_id 保存一个文档而不是更新):

    req.session.user = new UserModel(req.session.user);
    req.user.name = 'James';
    req.user.save( ... );
    

    【讨论】:

      【解决方案2】:

      Express 会话存储为单独的存储,并且对可以存储的内容有限制。它不能存储原型数据(方法)。看它更像key<>value storage。

      这样做的原因是会话可以存储在任何地方(几乎),例如:进程内存;数据库(mongodb);雷迪斯;等等

      因此,一旦您在会话对象中放入任何内容并且请求完成,express 将获取会话数据,对其进行解析并像纯对象一样粘贴到会话存储中。默认情况下它是进程内存,但如前所述 - 可以是任何其他存储。

      因此,当下一个 http 请求发生时,会话中间件将尝试使用请求标头中的 cookie(如果使用)数据来恢复会话,并要求会话存储返回数据。因为它已经格式化了——它不会有 .prototype 的东西,只有数据(objectarraystringnumber 和其他简单的东西)。

      因此,您需要再次使用该数据创建模型。
      您可以通过中间件在 express 中完成。所以在app.configure 定义session 之后你可以这样做:

      app.use(function(req, res, next) {
        if (req.session && req.session.user) {
          req.session.user = new UserModel(req.session.user);
        }
        return next();
      });
      

      这将检查是否定义了会话以及用户。然后将根据这些数据重新创建猫鼬模型。

      正如 cmets 中所述 - 您不应将用户数据存储在会话中,因为由于数据所有权的责任分工,这会导致不一致。 所以只存储用户的ID,然后使用中间件从数据库中加载它。

      app.use(function(req, res, next) {
        if (req.session && req.session.userID) {
          UserModel.findById(req.session.userID, function(err, user) {
            if (!err && user) {
              req.user = user;
              next();
            } else {
              next(new Error('Could not restore User from Session.'));
            }
          });
        } else {
          next();
        }
      });
      

      之后你可以创建中间件来检查用户是否登录:

      function userRequired(req, res, next) {
        if (req.user) {
          next();
        } else {
          next(new Error('Not Logged In');
        }
      }
      

      然后像这样使用它:

      app.get('/items', userRequired, function(req, res, next) {
        // will only come here if user is logged in (req.user !== undefined)
      });
      

      【讨论】:

      • 谢谢!根据帖子,我已经在制作一些中间件来重新创建用户作为解决方法(我已将现有代码添加到问题中)。目前我每次都查找数据库,一个从 request.sesion.user 中“重新水化”的新模型实例将无法正确保存(),因为 ID 已经存在。我可以使用您的解决方案并执行 findByIdAndUpdate() 而不是 save() 但这似乎真的很hacky。
      • 老实说 - 你可以只保留会话中的用户 ID,而不是所有数据,并且每次都加载。像 passport.js 这样的大多数解决方案都是这样做的。由于 mongo 中的 ID 已编入索引 - 此查询非常有效。依赖一个数据库而不是会话更好,因为用户数据可以同时更改,而会话不会知道。所以依靠数据库而不是会话来获取数据 - 是更稳定的解决方案。
      • 不客气。我添加了如何将用户从 ID 恢复为中间件的代码,如果从会话中恢复用户时出错。
      • 这个答案你应该得到另一个 +500,这与我遇到的问题完全相同。只保存 user_id 是有意义的,但我担心服务器开销。一个好处是当用户更新他/她的信息时,它会立即激活,不需要手动刷新。感谢您提供非常有帮助的答案!
      【解决方案3】:

      即使它有效(由于 Maksims 详述的原因,它不会有效),在会话中持久化用户模型实例也是一个坏主意。如果同一用户多次登录,这两个会话的用户实例将不同步并不断相互覆盖。

      正确的做法是将用户的_id 持久化在会话中(例如request.session.userId)并安装Express 中间件以将用户模型实例加载到request.user 中,这样您的其他中间件和当前呼叫的路由处理程序。与您现在所做的类似,但更干净一些。

      通过_id 查找单个对象的速度非常快,而且因为您经常这样做,所以它已经在内存中,因此开销为零。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-28
        • 1970-01-01
        • 2017-08-11
        • 2016-12-19
        • 2015-09-08
        • 2021-08-04
        • 2018-11-09
        相关资源
        最近更新 更多