【问题标题】:ExpressJS Middleware - Execution order of Promises not correctExpressJS 中间件 - Promises 的执行顺序不正确
【发布时间】:2022-01-14 15:16:02
【问题描述】:

我想创建一个快速中间件来执行基本检查授权标头中的用户/密码对是否存在于 JSON 文件中(教育目的)。我将它添加到一个非常简单的单位转换器应用程序中。

问题是当用户名/密码正确时,我收到的是 403 而不是资源。

我发现当我执行请求时,中间件中的 Promise.then 在我的函数 findUserByCredentials 中实现 Promise 之前执行。请参阅下面第三个代码 sn-p 中的问题说明。

index.js

const express = require('express')
const app = express()
const port = process.env.PORT || 3000
const findUserByCredentials = require("./lib/find-user");

app.use(function (req, res, next) {
    if (req.headers) {
        let header = req.headers.authorization || '';
        let [type, payload] = header.split(' ');

        if (type === 'Basic') {
            let credentials = Buffer.from(payload, 'base64').toString('ascii');
            let [username, password] = credentials.split(':');
            findUserByCredentials({username, password}).then(() => {
                console.log("next")
                next();
            }).catch(() => {
                console.log("403")
                res.sendStatus(403);
            });
        }
    } else {
        next();
    }
});

app.get('/inchtocm', (req, res) => {
    const cm = parseFloat(req.query.inches) * 2.54;
    res.send({"unit": "cm", "value": cm});
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

module.exports = app;

./lib/find-user.js

const bcrypt = require('bcrypt');
const jsonfile = require('../users.json');

let findUserByCredentials = () => (object) => {
    const username = object.username;
    const password = object.password;
    return new Promise((resolve, reject) => {
        jsonfile.forEach(user => {
            if (user.username === username) {
                bcrypt.compare(password, user.password).then((buffer) => {
                    if (buffer) {
                        console.log("resolve")
                        resolve();
                    } else {
                        console.log("reject")
                        reject();
                    }
                });
            }
        });
        reject();
    });
};
module.exports = findUserByCredentials();

发送请求后的服务器控制台

Example app listening at http://localhost:3000
403
resolve

如何强制 Express 在执行第二个操作之前等待第一个 Promise 完成?

【问题讨论】:

    标签: javascript node.js express


    【解决方案1】:

    为了更好地控制 promise 的顺序并减少嵌套代码,您应该使用 async/await 语法。你可以阅读更多关于它的信息here

    它的本质是让你……好吧,在继续之前等待异步操作完成。如果您在返回 Promise 的东西之前使用 await(例如您的 findUserByCredentials),它将分配给变量您解决承诺的内容,例如:

      const myPromise = () => {
        return new Promise(resolve => resolve(3));
      }
    
      const myFunc = async () => {
        const number = await myPromise(); 
        console.log(number); // Output: 3
      }
    
    

    我会改写你的 findUserByCredentials 函数,如下所示:

    const bcrypt = require('bcrypt');
    const jsonfile = require('../users.json');
    
    const findUserByCredentials = async (object) => {
        const username = object.username;
        const password = object.password;
        // This assumes there's only a single unique user with a specific username
        const potentialUser = jsonfile.find(user => user.username === username);
        if (!potentialUser) {
           throw new Error('wrong credentials')
        }
        const passHashCompare = await bcrypt.compare(password, potentialUser.password);
        if (!passHashCompare) {
           throw new Error('wrong credentials')
        }
    };
    module.exports = findUserByCredentials;
    
    

    这种方式嵌套更少,可读性更高,并且可以按照您需要的顺序工作。 你可以更进一步地遵循这个原则,让你的中间件(你传递给app.use的函数)也成为async function,并使用await关键字代替.then().catch()

    【讨论】:

      【解决方案2】:

      我会切换代码以使用与异步代码async/await 相关的 javascript 语言的最新功能,这样您就可以更好地控制您的执行流程。

      我会通过以下方式修改你的代码:

      首先对于 findUserByCredentials 函数:

      const bcrypt = require('bcrypt');
      const jsonfile = require('../users.json');
      
      const findUserByCredentials = async (object) => {
          const username = object.username;
          const password = object.password;
          const user = jsonfile.find(user => user.username == username);
          if (user)
            await bcrypt.compareSync(user.password, password);
          return false;
          
      };
      module.exports = findUserByCredentials;
      

      其次是index.js

      const express = require('express')
      const app = express()
      const port = process.env.PORT || 3000
      const findUserByCredentials = require("./lib/find-user");
      
      app.use(async (req, res, next) => {
          if (req.headers) {
              let header = req.headers.authorization || '';
              let [type, payload] = header.split(' ');
      
              if (type === 'Basic') {
                  const credentials = Buffer.from(payload, 'base64').toString('ascii');
                  const [username, password] = credentials.split(':');
                  const result = await findUserByCredentials({username, password})
                  if(result) {
                    return next()
                  } 
                  return res.sendStatus(403);
              }
              return res.sendStatus(403);
          }
          return next();
      });
      
      app.get('/inchtocm', (req, res) => {
          const cm = parseFloat(req.query.inches) * 2.54;
          res.send({"unit": "cm", "value": cm});
      });
      
      app.listen(port, () => {
          console.log(`Example app listening at http://localhost:${port}`)
      })
      
      module.exports = app;
      

      了解如何在 Nodejs 中使用异步代码非常重要,因为它是单线程,如果使用不正确,您可以阻止它,并且您的应用程序的性能不会达到最佳,async/await 也是 javascript 的语法糖允许编写干净的异步代码的语言,请尝试使用语言规范中的最新内容,因为它们是为了简化我们的生活而创建的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-03
        • 1970-01-01
        • 1970-01-01
        • 2012-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多