如何在嵌套Promise中使用请求对象?

时间:2019-06-15 23:58:18

标签: javascript express promise callback

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

我尝试了各种关于如何将变量传递到JavaScript Promise的选项,但是所有这些都要求您自己定义Promise,这不是我的情况。我还尝试了等待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')
    })
})

在此先感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

我相信req之所以未定义的原因是,在执行中间件(res,req,next)=> {...}之后,promise被解决了。尝试返回顶级承诺,即(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您正在运行一个异步函数,因此,express它要等到您的中间件解决后再继续进行下一个中间件。

答案 1 :(得分:0)

您有两个嵌套的异步操作(此处简化显示),然后尝试在第一个.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)在return之前添加client.callback(),以便将内部承诺链接到外部承诺。然后,直到完成内部的一个,外部的一个才会完成,您可以添加另一个.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);
    });
});

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