【问题标题】:NodeJS/express 4 : Can't set headers after they are sent while writing cookieNodeJS / express 4:在写入cookie时发送后无法设置标题
【发布时间】:2014-09-08 10:47:03
【问题描述】:

我正在设置一个模块以通过检查 cookie 来授予身份验证。 在每条路由上调用此模块,如果没有经过身份验证的会话,它将把 cookie 与数据库进行比较并授予会话。 每次成功比较后,模块都会更新数据库和 cookie 信息,这就是我收到错误“错误:发送后无法设置标头”的地方。

所以基本上我有我的路线“索引”:

var check_auth = require('./middleware/check_auth');
module.exports = function(app){

    app.get('/', check_auth, function(req, res){
        if(req.session.userid){
            res.render('index', { title: 'AUTH'});  
        }else{
            res.render('index', { title: 'NOT AUTH'});
        }
    });
};

现在是我的模块 check_auth :

var mysql = require('mysql');
var crypto = require('crypto');
var bcrypt = require('bcrypt');
var pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: '****',
    database: 'nodejs'  
});

function check_auth(req, res, next){
    if(!req.session.userid){
        var cookie_auth = req.signedCookies.auth;
        var cookie_db_id = req.signedCookies.db_id;
        if(cookie_auth && cookie_db_id){
            pool.getConnection(function(err, conn) {
                var db_id = false;
                query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
                query.on('error', function(err){
                    throw err;
                });
                query.on('result', function(row){
                    db_id = row.id;
                    db_cookie = row.cookie;
                    db_username = row.username;
                });
                query.on('end', function(result){
                    if(db_id && cookie_auth == db_cookie){
                        console.log("OK");
                        req.session.username = db_username;
                        req.session.userid = db_id;
                        var salt = bcrypt.genSaltSync(10);
                        var crypt = crypto.randomBytes(64).toString();
                        var hash = bcrypt.hashSync(crypt, salt);                    
                        res.cookie('auth', hash, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });                          
                        res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
                        pool.getConnection(function(err, conn) {                        
                            conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info){
                                if(err) throw err;
                            });
                        });
                        conn.release();                             
                    }
                });
            });
        }
    }
    next();
}

module.exports = check_auth;

错误出现在以下行:

res.cookie('auth', hash, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });                          
res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });

这个错误是因为我正在将我的 cookie 从 mysql 写入异步函数还是与模块/路由有关? 无论如何,欢迎任何解决方案。

【问题讨论】:

  • 由于异步特性,next 很可能会立即被调用。将您的回调移动到您的 end 处理程序中。
  • 谢谢!!工作正常!您可以添加为答案,以便我接受它
  • 另外,不要在应用的回调中抛出错误。通过return next(err);使用node.js/express的错误传递方式。
  • @AlexandruB 好的,我会的。猜猜它显示了快速渲染的错误,对吧?
  • 如果您希望在渲染中完成错误,您必须添加自己的特殊逻辑,它不是自动的。相反,Express offers a default error handler 添加到 app 本身作为带有四个参数的最后一个回调。类似于:app.use(function (err, res, req, next) { ...logic... });。在您的逻辑中,您可以根据错误进行渲染。我建议创建自己的 Error 类,该类在下面使用 JavaScript Error 对象,但包含更多数据。

标签: node.js cookies express


【解决方案1】:

由于异步特性,next 很可能会立即被调用。将您的回调移动到您的 end 处理程序中。

function check_auth(req, res, next) {
    if (!req.session.userid) {
        var cookie_auth = req.signedCookies.auth;
        var cookie_db_id = req.signedCookies.db_id;
        if (cookie_auth && cookie_db_id) {
            pool.getConnection(function(err, conn) {
                if(err) return next(err);
                var db_id = false;
                query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
                query.on('error', function(err) {
                    return next(err);
                });
                query.on('result', function(row) {
                    db_id = row.id;
                    db_cookie = row.cookie;
                    db_username = row.username;
                });
                query.on('end', function(result) {
                    if (db_id && cookie_auth == db_cookie) {
                        console.log("OK");
                        req.session.username = db_username;
                        req.session.userid = db_id;
                        var salt = bcrypt.genSaltSync(10);
                        var crypt = crypto.randomBytes(64).toString();
                        var hash = bcrypt.hashSync(crypt, salt);
                        res.cookie('auth', cookie_auth, {
                            expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)),
                            signed: true,
                            httpOnly: true,
                            secure: true
                        });
                        res.cookie('db_id', db_id, {
                            expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)),
                            signed: true,
                            httpOnly: true,
                            secure: true
                        });
                        pool.getConnection(function(err, conn) {
                            conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info) {
                                if (err) throw err;
                            });
                        });
                        conn.release();
                    }
                    return next();
                });
            });
        }
    } else {
        next();
    }
}

【讨论】:

  • 嘿抱歉,我第一次移动下一个时它起作用了,但现在我清除了浏览器的所有会话/cookie,就像它等待下一个永远不会出现的语句一样。即使我有 cookie 但没有会话
  • 已编辑,尝试将其移到 if 语句之外。如果您依赖多个操作,您很可能还需要多个操作。
  • 是的,我在第一个 if 中也有下一个声明(我的评论中没有提到)。无论如何,我明白了。必须在所有 if 语句中添加 next() :) Ty
【解决方案2】:

是的,您对问题的异步性质是正确的。一个简单的解决方法是在查询结束时调用呈现功能:

function check_auth(req, res, next){
if(!req.session.userid){
    var cookie_auth = req.signedCookies.auth;
    var cookie_db_id = req.signedCookies.db_id;
    if(cookie_auth && cookie_db_id){
        pool.getConnection(function(err, conn) {
            var db_id = false;
            query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
            query.on('error', function(err){
                throw err;
            });
            query.on('result', function(row){
                db_id = row.id;
                db_cookie = row.cookie;
                db_username = row.username;
            });
            query.on('end', function(result){
                if(db_id && cookie_auth == db_cookie){
                    console.log("OK");
                    req.session.username = db_username;
                    req.session.userid = db_id;
                    var salt = bcrypt.genSaltSync(10);
                    var crypt = crypto.randomBytes(64).toString();
                    var hash = bcrypt.hashSync(crypt, salt);                    
                    res.cookie('auth', cookie_auth, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });                           
                    res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
                    pool.getConnection(function(err, conn) {                        
                        conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info){
                            if(err) throw err;
                        });
                    });
                    conn.release();
                    if(req.session.userid){
                        res.render('index', { title: 'AUTH'});  
                    }else{
                        res.render('index', { title: 'NOT AUTH'});
                    }
                }
            });
        });
    }
}
next();}`

【讨论】:

  • 在一个函数中完成所有逻辑是不好的做法,尤其是当 Node.js 允许智能中间件结构和代码重用时。
  • @AlexandruB 同意,这就是我使用带有路由的模块的主要目的。
猜你喜欢
  • 1970-01-01
  • 2018-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 2018-02-21
相关资源
最近更新 更多