Я знаю, что на это ответили 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.





Выяснил мою проблему, проблема была next() ближе к концу, она вызывалась после res.send/json, который пытался снова передать/установить заголовки.
Предложите использовать новый await/async, стиль обратного вызова подвержен ошибкам, трудности с организацией асинхронного управления следуют.
Express framework - это стиль обратного вызова, вы можете использовать https://github.com/tj/co внутри ваш обработчик для использования await/async, но, наконец, http://koajs.com/ лучше.
Используйте util.promisify nodejs.org/api/util.html#util_util_promisify_original для переноса API стиля обратного вызова в обещание, используйте github.com/tj/co для управления всеми API стиля обещания/асинхронного/ожидания в вашей структуре стиля обратного вызова. Вы можете медленно реорганизовать свой старый проект в стиле обратного вызова.
Поскольку вы разобрались со своей проблемой, не стесняйтесь отметить свой ответ как правильный ответ!
Я пошел дальше и написал фрагмент кода выше, используя синтаксис 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.
Я также предполагаю, что вы используете Mongo на основе ваших вызовов функций в вашем коде. А поскольку функции Mongo теперь поддерживают await/async, вам не нужно использовать какие-то причудливые библиотеки для переноса ваших вызовов Mongo!
Спасибо за пример кода и за правильную пометку моего ответа, SO ограничивает вас в этом, я думаю, что мне потребуется 72 часа, прежде чем я смогу правильно его отметить, так что для меня еще около 12 часов. Я обязательно прочитаю эти ссылки, TY
@JoshKirby, отлично! Дайте знать, если у вас появятся вопросы. Кроме того, если вы нашли мой ответ полезным, не стесняйтесь также проголосовать за него. :)
Я все еще изучаю обещания, так как мне сказали, что лучше всего изучить их, прежде чем я буду делать асинхронное ожидание. Так что я доберусь туда вовремя, но на данный момент я использую обратные вызовы только потому, что они мне известны, и я хочу выпустить это раньше, чем позже. Время позже, чтобы переписать программу на более новые методы, которые я изучил. Может быть, вы могли бы привести пример того, как я бы переписал это, чтобы использовать async/await, чтобы я мог видеть?