5

I am new to nodeJS and asynchronous programming. I am using express as the base for my app, there is really only one route that serves a page and accepts an upload from a form. I would like to make a POST request to an external service after the the file has been uploaded. Attempting to execute any code after res.send(200) however results in an error message of Error: Can't set headers after they are sent.

I am using the request package to make the post to the external service.

var express = require('express');
var router = express.Router();
var util = require("util");
var request = require("request");

/* POST uploads. */
router.post('/', function(req, res, next) {
    console.log("LOG:" + util.inspect(req.files));
    res.send('respond with a resource');
    postFile(req.files.file.path);
});

var postFile = function(path) {
    var opts = {
        method: 'POST',
        uri: 'https://example.com/api/files.upload',
        formData: {
            token: "xxx",
            file: fs.createReadStream(req.files.file.path)
        }
    }

    // console.log("LOG:\n" + util.inspect(opts));

    request.post(opts, function(error, response, body) {
        if (error) {
            console.log("ERROR LOG: " + util.inspect(error));
        } else {
            console.log("RESPONSE LOG:" + util.inspect(response));
        }
    });
}

module.exports = router;

The postFile function works fine on it's own and even adding a console.log directly after the res.send results in the same error. How can I continue to execute code on the server after the response has been sent to the client?

Output log from node:

POST /uploads 200 85.201 ms - 23 Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11) at ServerResponse.header (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:700:10) at ServerResponse.send (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:154:12) at fn (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:934:10) at View.exports.renderFile [as engine] (/Users/jason/dev/test/file-share/node_modules/jade/lib/index.js:374:12) at View.render (/Users/jason/dev/test/file-share/node_modules/express/lib/view.js:93:8) at EventEmitter.app.render (/Users/jason/dev/test/file-share/node_modules/express/lib/application.js:566:10) at ServerResponse.res.render (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:938:7) at /Users/jason/dev/test/file-share/app.js:58:7 at Layer.handle_error (/Users/jason/dev/test/file-share/node_modules/express/lib/router/layer.js:58:5)

jfriend00
  • 683,504
  • 96
  • 985
  • 979
Jason Yost
  • 4,807
  • 7
  • 42
  • 65
  • One programming mistake is that `postFile()` should use the `path` argument passed to it, not `req.files.file.path`. Probably not the cause of your issue. – jfriend00 Mar 05 '15 at 03:18
  • If you look at the stack trace, it is from `handle_error`, which means that express caught an error and tried to send an error response. The `req.files.file.path` error @jfriend00 mentioned is likely causing the function to throw, so your `res.send` sends and closes the connection, and then the syntax error tries to send an error page. Comment out `res.send` and you will see the actual error. – loganfsmyth Mar 05 '15 at 03:33

2 Answers2

2

There's nothing wrong in the code below, you can execute code after responding the request. You just can't send headers again.

router.post('/', function(req, res, next) {
  console.log("LOG:" + util.inspect(req.files));
  res.send('respond with a resource');
  postFile(req.files.file.path);
});

Using the request to POST or GET something will not respond to your request, unless you want to.

Patrick
  • 140
  • 6
  • I would think so too, yet the fact remains that even using console.log after res.send returns the same error. It may be that I have somehow setup my project wrong. – Jason Yost Mar 05 '15 at 02:34
  • Maybe this error message is coming from another part of your code. – Patrick Mar 05 '15 at 02:45
  • It does nothing other than this. It accepts a file and attempts to upload it. removing postFile(req.files.file.path); after res.send() and the code works fine, add console.log('test'); after res.send() and the same error message is produced. – Jason Yost Mar 05 '15 at 02:47
0

Your postFile() function is referring to req.files.file.path in this line:

file: fs.createReadStream(req.files.file.path)

but the req object is not being passed to it. This should generate some sort of exception.

It appears you should be using just:

file: fs.createReadStream(path)

since you are passing the path to postFile(path).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks for pointing that out I was excited for a moment when I thought it might have just been a boneheaded mistake. However, changing it does not change the response. Entirely removing the call and replacing it with a simple console.log does not change the response. – Jason Yost Mar 05 '15 at 03:57