Заголовки уже отправлены, но не уверены, почему - PullRequest
0 голосов
/ 15 февраля 2019

Я знаю, что на это отвечали тысячи раз, но я не могу на всю жизнь понять, почему он пытается отправлять заголовки более одного раза.Поэтому, пожалуйста, если вы собираетесь пометить как дубликат, объясните, почему это дубликат и где я ошибся.Ссылка редко бывает полезна без объяснения причин.

Хорошо, моя проблема.У меня есть простой маршрут подтверждения, который запускает промежуточное программное обеспечение / контроллер, когда я пытаюсь повторно отправить токен подтверждения во второй раз, чтобы реплицировать пользователя, щелкающего ссылку подтверждения, во второй раз, когда он сообщает мне строку, рядом с которой я указал, вызывает повторную отправку заголовков.

Токен для пользователя все еще находится в моей БД (планируйте изменить это), но это не должно иметь значения, поскольку строка, которая, кажется, вызывает ошибку, просто проверяет профиль пользователя, чтобы увидеть, проверен ли он.

router.post('/confirmation', 
  user.confirmationPost);


exports.confirmationPost = function (req, res, next) {
    // Find a matching token
    Token.findOne({ token: req.body.token }, function (err, token) {
        if (!token) return res.status(400).send({ type: 'not-verified', message: 'We were unable to find a valid token. Your token my have expired.' });

        // If we found a token, find a matching user
        User.findOne({ _id: token._userId }, function (err, user) {
            if (!user) return res.status(400).send({ message: 'We were unable to find a user for this token.' });
            if (user.isVerified) return res.status(400).send({ message: 'This user has already been verified.' }); // THIS LINE CAUSES THE ERROR

            // Verify and save the user
            user.isVerified = true;
            user.save(function (err) {
                if (err) { return res.status(500).send({ message: err.message }); }
                res.redirect(`${config.siteURL}/dash`);
            });
        });
    });
    next();
};

Сообщение об ошибке

Error: Can't set headers after they are sent. 

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

Предложите использовать новый await / async, стиль обратного вызова подвержен ошибкам, сложность организации асинхронного управления следует.

Express-фреймворк - это стиль обратного вызова, вы можете использовать https://github.com/tj/co внутри вашегообработчик для использования await / async, но в итоге http://koajs.com/ лучше.

0 голосов
/ 15 февраля 2019

Поскольку вы выяснили свою проблему, не стесняйтесь пометить свой ответ как правильный ответ!

Я написал свой фрагмент кода выше, используя синтаксис ES7 async / await.Хотя код может выглядеть длиннее, его легче понять.Я также расширил все функции возврата и добавил комментарии, которые способствовали увеличению длины.

/*
 * Notice how the keyword `async` is in front of the arrow function. 
 * This tags the function as asynchronous and returns a promise. 
 * It also allows us to use the `await` keyword which waits for the function 
 * to return whatever follows the await keyword. Only after it returns will 
 * the function continue.
 */ 
exports.confirmationPost = async (req, res, next) => {
    // The await keyword here makes sure that Token.findOne() returns (resolves/rejects promise) before continuing the function.
    // However, this DOES NOT mean that the program hangs, as it would for a synchronous function. 
    // Other functions can run while this instance of the function waits for Token.findOne() to return.
    let token = await Token.findOne({ token: req.body.token });

    // Once Token.findOne returns, you have access to a then callback.
    token.then((err, token) => {
        if (!token) { 
            // While this is completely fine, I suggest you use next() and pass in an error obj to it and let your middleware handle the return of the error to the client.
            return res.status(400).send({ 
                type: 'not-verified', 
                message: 'We were unable to find a valid token. Your token my have expired.' 
            });
        }

        // Once again, we use await to wait for User.findOne() to return.
        let user = await User.findOne({ _id: token._userId });

        // Once User.findOne returns, you have access to a then callback.
        user.then((err, user) => {
            if (!user) {
                return res.status(400).send({ 
                    message: 'We were unable to find a user for this token.' 
                });
            }
            if (user.isVerified) {
                return res.status(400).send({ 
                    message: 'This user has already been verified.' 
                }); 
            }

            // Verify and save the user
            user.isVerified = true;

            // You can simply chain the then callback instead of making a new variable.
            user.save().then(err => {
                if (err) { 
                    return res.status(500).send({ 
                        message: err.message 
                    }); 
                }
                res.redirect(`${config.siteURL}/dash`);
            });
        });
    });
}

Обратите внимание на эти две ссылки, приведенные ниже, которые действительно коснутся гвоздя на голове, когда речь заходит об использовании обещаний и асинхронности / ожидания с Node и Mongo.

  1. https://medium.com/@rossbulat/using-promises-async-await-with-mongodb-613ed8243900
  2. http://www.codingpedia.org/ama/cleaner-code-in-nodejs-with-async-await-mongoose-calls-example
0 голосов
/ 15 февраля 2019

Разобрался с моей проблемой, проблема была next() ближе к концу, она вызывалась после res.send / json, который снова пытался передать / установить заголовки.

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