Как связать условный асинхронный обратный вызов? - PullRequest
0 голосов
/ 05 сентября 2018

В экспресс-проекте node.js я хочу реализовать функцию переключения на пользователя для пользователей с правами администратора. Администратор может ввести имя пользователя или идентификатор пользователя в поле. Ниже приведен код для обработки этого запроса. Проблема в том, что если первый вызов базы данных не удался, мне нужно повторить его с другим видом запроса, прежде чем продолжить с кодом входа в систему. Как добавить условный асинхронный вызов перед вызовом на req.login?

router.route('/switchuser')
    .post(function (req, res) {
        mongoose.model('Users').findById(req.body.idOrName, function (err, user) {
            if (!user) {
                mongoose.model('Users').findOne({ username: req.body.idOrName }, function (err, user_) {
                    user = user_;
                    if (err) {
                        res.status(404);
                        res.send("User not found " + err);
                    }
                });
            }
            req.login(user, function (err) {
                if (err) {
                    res.status(404);
                    res.send("There was a problem switching the user: " + err);
                }
                else {
                    res.format({
                        html: function () {
                            res.redirect("/");
                        }
                    });
                }
            })
        });
    });

1 Ответ

0 голосов
/ 05 сентября 2018

Вы испытываете ад обратного вызова. Когда пользователь не найден по ID, вы запускаете второй обратный вызов. Проблема заключается в том, что вы входите в систему, поскольку метод выполняется вне этого второго обратного вызова, что означает, что он не ожидает ответа. Обратный вызов ада может быстро вызвать много дублирующегося кода. Одно из решений состоит в том, чтобы вытянуть ваши общие куски кода в функции. Ниже приведен один пример того, как вы могли бы сделать это. Обратите внимание, что loginAs может вызываться в двух разных точках в зависимости от того, был ли пользователь найден по ID или требовался дополнительный поиск.

router.route('/switchuser')
    .post(function (req, res) {
        mongoose.model('Users').findById(req.body.idOrName, function (err, user) {
            if (err) {
                sendErrorResponse(err);
                return;
            }

            if (user) {
                loginAs(user, req, res);
                return
            }

            mongoose.model('Users').findOne({ username: req.body.idOrName }, function (err, user) {
                if (err) {
                    sendErrorResponse(err, req, res);
                    return;
                }

                if (!user) {
                    sendNotFoundResponse(req, res);
                    return;
                }

                loginAs(user, req, res);
            });
    });
});

function loginAs(user, req, res) {
    req.login(user, function (err) {
        if (err) {
            res.status(404);
            res.send("There was a problem switching the user: " + err);
        }
        else {
            res.format({
                html: function () {
                    res.redirect("/");
                }
            });
        }
    })
}

function sendErrorResponse(err, req, res) {
    res.status(500);
    res.send("Failed to switch user " + err);
}

function sendNotFoundResponse(req, res) {
    res.status(404);
    res.send("Could not find user");
}

Теперь, в зависимости от того, какую версию Mongoose JS вы используете, ограниченная версия Promises может быть доступной уже . Ваш код может быть очищен еще дальше, вот так.

router.route('/switchuser')
    .post(function (req, res) {
        mongoose.model('Users').findById(req.body.idOrName)
            .then(function (user) {
                // If the user was found, return it down the chain
                if (user) {
                    return user;
                }

                // If the user was not found, return the promise for the next query
                return mongoose.model('Users').findOne({ username: req.body.idOrName });
            })
            .then(function(user) {
                if (!user) {
                    sendNotFoundResponse(req, res);
                    return;
                }

                loginAs(user, req, res);
            })
            .catch(function(err) {
                sendErrorResponse(err, req, res);
            });
    });
});

function loginAs(user, req, res) {
    req.login(user, function (err) {
        if (err) {
            res.status(404);
            res.send("There was a problem switching the user: " + err);
        }
        else {
            res.format({
                html: function () {
                    res.redirect("/");
                }
            });
        }
    })
}

function sendErrorResponse(err, req, res) {
    res.status(500);
    res.send("Failed to switch user " + err);
}

function sendNotFoundResponse(req, res) {
    res.status(404);
    res.send("Could not find user");
}

Вы заметите, что с обещаниями вы можете объединить все ошибки внизу в один catch. Более чистый результат с меньшим количеством дублирующегося кода и вызовов методов. Возможно, вам придется выполнить exec, чтобы получить доступ к методу catch. Просто прочитайте документы Mongoose, и все будет в порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...