【问题标题】:How to persist Mongoose virtuals through Passport如何通过 Passport 持久化 Mongoose 虚拟对象
【发布时间】:2017-12-29 11:41:51
【问题描述】:

我找到了this 的帖子,它解释了在调用toObjecttoJSON 方法时如何保留虚拟字段。但是,我的问题是我正在尝试使用 Passport.js 本地策略设置的 req.user 对象,这(显然)不是 Mongoose 模型。

我有一个 User 架构来存储名字和姓氏,并且我使用虚拟属性 name.full 来调用全名,中间有一个空格。我不必为此使用虚拟,不,但我真的很想。

const schema = new Schema({
  name: {
    first: "john",
    last: "smith"
  }
})
  .virtual("name.full")
  .get(function () {
    return this.name.first + " " + this.name.last;
  });

module.exports = mongoose.model("User", schema);

现在,我有一个指向/login 的路由器,用户可以在其中发布他/她的登录信息,Passport.js 将使用本地策略进行身份验证。

用户通过身份验证后,我使用 Passport.js 自动设置的 req.user 对象将用户信息保存在 Express.js 会话中。

function redirectToHome (req, res) {
    req.session.contextVars = { user: req.user };
    res.redirect("/home");
}

所以整个后路由器看起来像:

router.post("/login", passport.authenticate("local", { failureRedirect: "login" }), redirectToHome);

这里的问题是,尽管 req.user 是一个对象,它可以做 Mongoose 模型可以做的所有事情——甚至调用实例方法——但它没有任何虚拟字段。我设置了toObject: { virtuals: true }toJSON: { virtuals: true },就像前面提到的帖子中所建议的那样,因为我认为 Passport.js 在将数据存储到会话对象中之前可能会在后台调用两者之一,但似乎并非如此。

我正在使用express-session passport.session() 进行会话管理。

app.js

const express = require("express")
    , app = express();
const session = require("express-session");
const passport = require("passport");

app.use(session({
    secret: "secret key",
    saveUninitialized: true,
    resave: true
}));

app.use(passport.initialize())
    .use(passport.session());

我正在尝试在pug 文件中读取此数据,该文件以Objectres.render 的形式接收数据。

routes/index.js

router.get("/home", function (req, res) {
    res.render("home", { LOCAL_DATA: req.session.contextVars });
});

home.pug

... usual head stuff
body
  p #{JSON.stringify(LOCAL_DATA.user)}

我可以想到一些解决方法,但我想以这种特定方式解决问题。有谁知道如何确保 Passport.js 为 req.user 对象分配了所有可见的虚拟字段?

谢谢。

【问题讨论】:

  • 没有持久性,因为代码在猫鼬模型中显示了它。如果我正确理解了您的查询,则您错过了对模式对象上 save 方法的调用。请参考我的回答。

标签: node.js mongoose passport.js passport-local


【解决方案1】:

根据猫鼬文档:

如果你使用 toJSON() 或 toObject() mongoose 将不包含虚拟对象 默认。这包括调用 JSON.stringify() 的输出 Mongoose 文档,因为 JSON.stringify() 调用了 JSON()。经过 { virtuals: true } 到 toObject() 或 toJSON()。

当 passport 将用户置于请求中时,默认情况下,它可能会在将有效负载发送回请求者之前使用 toJSON()toObject() 调用。

如果我没记错的话,我认为当您将其放在请求对象上时,您必须显式调用 toJSONtoObject() 并在您的 Passport 回调中传入 { virtuals: true }

【讨论】:

    【解决方案2】:

    如果我正确理解了您的问题,因为 mongoose 模型上没有保存调用,(您使用的 mongoose 模型只返回一个没有虚拟对象数据的模式。如测试所示,当调用静态 create 方法(在屏幕截图的测试代码中注释),测试通过,因为数据保存在虚拟对象中,并调用“用户”对象上的保存。

    因此,在创建数据并使用 save 方法将其持久化后,返回新对象而不是模式,如下所示:

    var mongoose = require('mongoose'),Schema=mongoose.Schema;
    var schema = new Schema({
      name: {
        first: "john",
        last: "smith"
      }
    })
      .virtual("name.full")
      .get(function () {
        return this.name.first + " " + this.name.last;
      });
    var NewUser = new mongoose.Schema(schema);
    // Below code added by Jv
    NewUser.statics.create = function (params, callback) {
      var newUpdate = new NewUser(params);
      newUpdate.save(function(err, result) {
        callback(err, result);
      });
      return newUpdate;
    };
    var User = mongoose.model('Model', NewUser);
    module.exports = User;
    

    【讨论】:

    • 哦,不,我正在使用user 实例,所以这是在调用create() 之后。虚拟属性 确实 存在,因为我可以在服务器端代码上对其进行测试。问题是当 Passport.js 尝试在其本地策略后将用户对象保存在req.user 中时,虚拟属性变得不可用。
    • 你在哪里使用下面的行? router.post("/login", passport.authenticate("local", { failureRedirect: "login" }), redirectToHome);可以分享一下代码来理解流程吗?
    • 可以分享一下代码理解流程吗?因为这将正确设置“req.user”的使用,或者它应该是“req.body.user”还是“req.params.user”
    • 恐怕我的代码是跨 3 个不同的文件编写的,它可能会使(已经很长)问题太长。 req.user 变量由 Passport.js 自动设置,并且使用的本地策略本质上要求我有一个保存的用户实例来进行身份验证。事实上,我知道 Passport.js 保存用户对象的方式存在问题。
    • 除虚拟财产外的一切都有效。不过,要回答您的问题,routes/index.js 中使用了该行,app.js 中需要该行。我解决了问题。
    猜你喜欢
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多