【问题标题】:How can I use the request object in a nested promise?如何在嵌套承诺中使用请求对象?
【发布时间】:2019-10-30 02:06:33
【问题描述】:

我正在使用 express 框架开发 Nuxt 服务器端呈现的应用程序。对于身份验证,我使用的是 openid-client 包。现在我想将检索到的令牌存储在快速会话中,但请求模型 (req) 在回调承诺中始终未定义。为此,我想使用 req.session.token = tokenSet.access_token。我是 JavaScript 的新手,所以我认为我遗漏了一些明显的东西。

我尝试了各种关于如何将变量传递到 JavaScript Promise 中的选项,但所有这些都需要您自己定义 Promise,这不是我的情况。我也尝试等待承诺并在回调承诺之外使用它,但也没有成功。

router.get('/api/oidc-callback', (req, res, params) => {
  Issuer.discover('http://localhost:5000') // => Promise
    .then(function(identityIssuer) {
      const client = new identityIssuer.Client({
      ...
      })
      // HERE IT IS DEFINED
      console.log(req)
      client
        .callback('http://localhost:3000/api/oidc-callback', req.query, {
          code_verifier
        })
        // => Promise
        .then(function(tokenSet) {
          // HERE IT IS UNDEFINED
          console.log(req)
          req.session.token = tokenSet.access_token
        }, req)
        .catch(error => {
          console.log(error)
        })
//Also tried using outside
      res.redirect('/oidc-callback')
    })
})

提前感谢您的帮助!

【问题讨论】:

  • 它并没有消失,并且在 then() 的范围内,所以看起来更像是您误解了问题
  • 我相信req 未定义的原因是因为promise 在中间件(res, req, next) =>{...} 执行后解决了。尝试返回顶级承诺,即(res, req, next) =>{ return Issuer.discover(...)},还在client.callback(..) 之前添加return 语句。让我知道它有效。
  • 你需要从内部then的参数列表中删除, req。只有错误处理程序(函数)应出现在该位置。可能无法解决问题,但不会造成伤害。
  • @macphilips 添加返回语句有效,现在更有意义了!谢谢
  • @nils168 不客气。我将提取我的评论作为这个问题的答案,并接受它作为正确答案

标签: javascript express promise callback


【解决方案1】:

我相信 req 未定义的原因是因为在执行中间件 (res, req, next) =>{...} 之后承诺已解决。尝试返回顶级承诺,即 (res, req, next) =>{ return Issuer.discover(...)},还在 client.callback(...) 之前添加一个 return 语句。

router.get('/api/oidc-callback', (req, res, params) => {
  return Issuer.discover('http://localhost:5000') // <-- added return here
    .then(function(identityIssuer) {
      const client = new identityIssuer.Client({
      ...
      })
      // HERE IT IS DEFINED
      console.log(req)
      return client  // <-- added return here
        .callback('http://localhost:3000/api/oidc-callback', req.query, {
          code_verifier
        })
        // => Promise
        .then(function(tokenSet) {
          // HERE IT IS UNDEFINED
          console.log(req)
          req.session.token = tokenSet.access_token
          res.redirect('/oidc-callback')
        }) // removed , req here, it is not needed
        .catch(error => {
          console.log(error)
        })
    })
})

通过添加 return 语句,它告诉 express 你正在运行一个异步函数,因此表示它会等到你的中间件解决后再继续下一个中间件。

【讨论】:

  • 这根本行不通。您已将res.redirect() 放在return 语句之后的同一函数中。它永远不会被执行。通过在return client.callback() 前面添加return,您走在正确的道路上,但必须进行其他更改才能正常工作,因此其余答案不正确。您可以在下面查看我的答案以获得完整的解释以及应该可以使用的修改后的代码。
【解决方案2】:

您有两个嵌套的异步操作(此处显示为简化),然后您尝试在第一个 .then() 处理程序中最后做一些事情:

Issuer.discover().then(function() {
    client.callback().then(function() {
       // ...
    });
    res.redirect('/oidc-callback');
});

这会导致res.redirect()client.callback() 完成之前被调用,因为您的代码中没有任何内容可以等待client.callback() 完成。在您修改会话之前发送响应并触发重定向,这不是您想要做的。您可以通过以下两种方式之一解决此问题:

1) 将res.redirect() 放在内部.then() 中,如下所示:

Issuer.discover().then(function() {
    client.callback().then(function() {
       // ...
       res.redirect('/oidc-callback');
    });
});

2) 在client.callback() 之前添加一个return,以便它将内部承诺链接到外部承诺。然后,在内部完成之前,外部不会完成,您可以添加另一个 .then() 处理程序以将 res.redirect() 放入:

Issuer.discover().then(function() {
    return client.callback().then(function() {
       // ...
    });

}).then(function() { // 当两个异步操作都完成时被调用 res.redirect('/oidc-callback'); });

我推荐选项 #2,因为它使错误处理更简单,因为您可以在顶层的一个地方完成所有错误处理。把所有这些放在一起,你会得到这个:

router.get('/api/oidc-callback', (req, res, params) => {
    Issuer.discover('http://localhost:5000').then(function(identityIssuer) {
        const client = new identityIssuer.Client({
            ...
        })
        return client.callback('http://localhost:3000/api/oidc-callback', req.query, {
            code_verifier
        }).then(function(tokenSet) {
            console.log(req);
            req.session.token = tokenSet.access_token
        }, req);
    }).then(() => {
        res.redirect('/oidc-callback');
    }).catch(err => {
        console.log(err);
        res.sendStatus(500);
    });
});

注意,我还在最后添加了适当的错误处理。这样可以确保在您的两个异步操作都完成之前不会发送任何响应,并且如果其中一个失败,它会发送正确的错误响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多