Заголовки уже отправлены, но не знаю, почему

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

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

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

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. 
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
54
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

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

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

Я все еще изучаю обещания, так как мне сказали, что лучше всего изучить их, прежде чем я буду делать асинхронное ожидание. Так что я доберусь туда вовремя, но на данный момент я использую обратные вызовы только потому, что они мне известны, и я хочу выпустить это раньше, чем позже. Время позже, чтобы переписать программу на более новые методы, которые я изучил. Может быть, вы могли бы привести пример того, как я бы переписал это, чтобы использовать async/await, чтобы я мог видеть?

joshk132 15.02.2019 04:18

Используйте util.promisify nodejs.org/api/util.html#util_util_promisify_original для переноса API стиля обратного вызова в обещание, используйте github.com/tj/co для управления всеми API стиля обещания/асинхронного/ожидания в вашей структуре стиля обратного вызова. Вы можете медленно реорганизовать свой старый проект в стиле обратного вызова.

tangxinfa 15.02.2019 04:29

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

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

/*
 * 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

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

Pas 15.02.2019 05:17

Спасибо за пример кода и за правильную пометку моего ответа, SO ограничивает вас в этом, я думаю, что мне потребуется 72 часа, прежде чем я смогу правильно его отметить, так что для меня еще около 12 часов. Я обязательно прочитаю эти ссылки, TY

joshk132 16.02.2019 15:41

@JoshKirby, отлично! Дайте знать, если у вас появятся вопросы. Кроме того, если вы нашли мой ответ полезным, не стесняйтесь также проголосовать за него. :)

Pas 19.02.2019 05:23

Другие вопросы по теме