标头已发送,但不确定为什么

时间:2019-02-15 02:13:40

标签: node.js express request

我知道这已经被回答了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. 

3 个答案:

答案 0 :(得分:1)

弄清楚我的问题,问题是next()接近尾声,它在res.send / json之后被调用,而res.send / json试图再次传递/设置标题。

答案 1 :(得分:0)

建议使用新的await / async,回调样式容易出错, 难以安排异步控制。

Express框架是回调样式,您可以在内部使用https://github.com/tj/co 您的处理程序以使用await / async,但最终http://koajs.com/会更好。

答案 2 :(得分:0)

既然您已解决问题,请随时将您的回答标记为正确答案!

我继续并使用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`);
            });
        });
    });
}

请查看下面的这两个链接,它们在使用Promise以及与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