David
David

Reputation: 593

node.js/express: Can't set headers after they are sent

Despite there being a ton of stackoverflow questions and articles online about this topic, none seem to apply to my particular application.

router.get('/getFile', (req, res) => {
    console.log("Calling getFile for file " + req.query.serialNumber + ".")
    var serialNumber = req.query.serialNumber;
    let request = new sql.Request(conn);
    request.query('SELECT FileName + \'.\' + FileExtension AS \'File\', FileType, ContentType, SerialNumber, Chart ' +
        'FROM dbo.ChangeFiles ' +
        'WHERE SerialNumber = ' + serialNumber)
        .then(function (recordset) {
            log("Successfully retrieved file " + recordset[0].SerialNumber + " from database.")
            res.writeHead(200, {
                'Content-Type': recordset[0].ContentType,
                'Content-disposition': 'attachment;filename=' + recordset[0].File
            });
            res.send(Buffer(recordset[0].Chart));
        }).catch(function (err) {
            log(err);
            res.send("Issue querying database!");
        });
});

I can't seem to figure out how to properly change the headers so that it is done before res.send hits. It is most likely due to the asynchronous nature of javascript, something I still have yet to grasp properly, but how can I ensure that res.writeHead is called before res.send?

Thank you so much in advance for looking at this.

Upvotes: 2

Views: 995

Answers (1)

peteb
peteb

Reputation: 19418

When using res.writeHead() the headers are immediately sent to the requester. Since you're using the Express function res.send(), which also sends over your headers, they have already been sent and you will get an error saying the headers have already been sent. Alternatively, you could use res.end() which will not cause a headers sent error since it will finalize the response and send whatever remaining chunks are passed to it.

how can I ensure that res.writeHead is called before res.send?

If you're going to use res.send(), change your res.writeHead() calls to res.setHeader() to set the header values on your response object. Additionally, when using Express you can set the status of the response using res.status(<statusCode>).

Another thing I noticed is you usage of Buffer. You should change your Buffer creation in the success response handler from Buffer() to Buffer.from() as Buffer() is deprecated.

router.get('/getFile', (req, res) => {
    console.log("Calling getFile for file " + req.query.serialNumber + ".")

    let serialNumber = req.query.serialNumber
    let request = new sql.Request(conn)
    let query = 'SELECT FileName + \'.\' + FileExtension AS \'File\', FileType, ContentType, SerialNumber, Chart ' +
        'FROM dbo.ChangeFiles ' +
        'WHERE SerialNumber = ' + serialNumber

    request.query(query)
        .then(recordset =>{
            console.log("Successfully retrieved file " + recordset[0].SerialNumber + " from database.")

            let {ContentType, Chart File} = recordset[0]

            // Set Response Status and Headers
            res.status(200)
            res.setHeader('Content-Type', ContentType)
            res.setHeader('Content-Disposition', `attachment;filename=${File}`)            

            return res.send(Buffer.from(Chart))
        }).catch(err => {
            console.log(err);
            return res.status(500).send("Issue querying database!");
        })
})

Upvotes: 4

Related Questions