azium azium - 5 months ago 34
Node.js Question

Express redirect error: can't set headers after they are sent

When this code hits the redirect line it throws the 'Can't set headers after they are sent error' and doesn't redirect. I'm guilty of not fully understanding headers and how express works with them. This link about this error is confusing me a bit, probably because I don't have a basic enough understanding of what's going on. Also, I know this is a bit of a naive approach to authenticating, but I'm just trying to get basic things to work.

app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
console.log("wrong pw")
}
})


UPDATE : thank you to @Brendan Ashworth I missed an obvious else, which I've added now and no longer get the error.

However this line doesn't change the contents of my page

res.sendfile('./public/admin/views/tunes.html')


It worked before I wrapped it with the auth check

var auth = require('../config/auth')

module.exports = function(app) {

/*
* CONTENT API
*/

//...

/*
* Admin Routes
*/
app.get('/admin/login', function(req, res) {
res.sendfile('./public/admin/views/login.html')
})

app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
res.json({message: 'Wrong password!'})
}
})

app.get('/admin', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/tunes.html')
console.log("test") //
} else { //added else
res.redirect('/admin/login')
}
})

app.get('/admin/:url', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/' + req.params.url + '.html')
} else { //added else
res.redirect('/admin/login')
}
})

// frontend routes
// route to handle all angular requests
app.get('*', function(req, res) {
res.sendfile('./public/views/index.html')
})


}

FINAL UPDATE!! The last thing I needed was to handle the redirect client side after sending the file. Simple authentication works perfectly now!

$http.post('/api/login', $scope.auth).success(function() {
window.location.href = '/admin'
})

Answer

An explanation of the error Can't set headers after they are sent error:

All HTTP responses follow this basic structure:

.. Response Line ..
.. Headers ..

.. Body ..

If you want to redirect a user, first the Response Line will be sent with a redirect code (lets say 300), then the Headers will be sent with a Location: xxx header.

Then, we can finally send a body (not in the case of a redirect, but in general). However - in the case with your code - you are sending a Body response then trying to redirect the user. Since the headers (and response line) have both already been sent (because you sent the body), it can't send more headers after the body.

An example of this in your code would be:

app.get('/admin', function(req, res) {
    if (auth.date) {
        res.sendfile('./public/admin/views/tunes.html')
    }
    res.redirect('/admin/login')
})

If I'm assuming right, you actually want to return after the res.sendfile() call. If auth.date is truthy, then you'll be sending a file (i.e. body response) and then giving a redirect code - that doesn't work.