Error: Can't set headers after they are sent using passportjs/Express

Error: Can't set headers after they are sent using passportjs/Express

有问题的错误

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

server.js

// set up ======================================================================
var express = require('express')
    , app = express()
    , cookieParser = require('cookie-parser')
    , bodyParser = require('body-parser')
    , expressSession = require('express-session')
    , server = require('http').createServer(app)
    , passport = require('passport')
    , local = require('passport-local').Strategy
    , md5 = require('md5')
    , util = require('util')
    , flash = require('connect-flash')
    , port = 80
    , url = require('url')
    , db = require('./db');

// optimization ================================================================
var compress = require('compression');
app.use(compress());

// configuration ===============================================================
app.use(express.static('public'));
app.use(cookieParser());
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.use(expressSession({
    secret: 'key',
    resave: false,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

// passport ====================================================================
passport.use(new local(
    function(username, password, done) {
        // asynchronous verification, for effect...
        process.nextTick(function () {
            db.findUserByName(username, function (err, user) {
                if (err) { done(err); }
                if (!user) { done(null, false, {message: 'Unknown user: ' + username})}
                if (md5(password) == user.password) {
                    done(null, user);
                } else {
                    done(null, false, {message: 'Invalid username or password'});
                }
            });
        });
    }
));
passport.serializeUser(function(user, done) {
    done(null, user.uuid);
});

passport.deserializeUser(function (id, done) {
    db.findUserByUUID(id, done);
});

// routes ======================================================================
require('./routes')(app, passport);

// launch ======================================================================
server.listen(port, function(){
    console.log('server started');
});

routes.js

var flash = require('connect-flash');

module.exports = function(app, passport) {

    app.get('/', function(req, res) {
        res.render('index', { num: 0, logged: false });
    });

    app.get('/login', function (req, res) {
        if (typeof req.user !== 'undefined') {
            // User is logged in.
            res.redirect('/');
        } else {
            req.user = false;
            var message = req.flash('error');
            if (message.length < 1) {
                message = false;
            }
            res.render('login', { logged: false, message: message });
        }
    });

    app.post('/login',
        passport.authenticate('local', {
            failureRedirect: '/login',
            failureFlash: true
        }),
        function(req, res) {
            res.redirect('/');
        }
    );

    app.get('/logout', function(req, res){
        req.logout();
        res.redirect('/');
    });


};

db.js

var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var url = 'mongodb://localhost:27017/db';

exports.findUserByName = function (username, callback) {
    MongoClient.connect(url, function(err, db) {
        var cursor = db.collection('players').find( { "name": username } );
        cursor.each(function(err, doc) {
            if (doc != null) {
                console.log('YES');
                callback(false, doc)
            } else {
                console.log('NO');
                callback(false, null);
            }
            db.close();
        });

    });
};

var findUserByUUID = function(uuid, db, callback) {
    var cursor = db.collection('players').find( { "uuid": uuid } );
    cursor.each(function(err, doc) {
        assert.equal(err, null);
        if (doc != null) {
            callback(false, doc)
        } else {
            callback(false, null);
        }
    });
};
exports.findUserByUUID = function (uuid, callback) {
    MongoClient.connect(url, function(err, db) {
        assert.equal(null, err);
        findUserByUUID(uuid, db, function(err, data) {
            callback(err, data);
            db.close();
        });
    });
};

我知道我调用了 done() 两次,但是什么时候?我找不到它。 估计是路由的问题,不知道是哪里。

在您的 passport.use() 处理程序中,每次调用 done() 时,您都需要 return 以便它之后的代码不会也被执行。

现在您有一系列 if 语句,每个语句都可以调用 done(),但它们不是 if/else 语句,因此您要测试的条件不止一个满足,因此 done() 可以被多次调用。

例如,更改为:

if (!user) { done(null, false, {message: 'Unknown user: ' + username})}

对此:

if (!user) { 
    done(null, false, {message: 'Unknown user: ' + username});
    return;
}

并且,改变这个:

if (err) { done(err); }

对此:

if (err) { 
    done(err); 
    return;
}

或者,您可以将所有内容都变成 if/else if/else if/else,这样只有一个分支可以执行。当您的多个 if 语句满足其条件然后被执行时,就会出现问题。


现在,您的 findUserByName()cursor.each() 中调用其回调,因此它可以多次调用它,然后您会多次调用 done()