Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have a NodeJS/connect middleware execute after responde.end() has been invoked?

I would like to achieve something like this:

var c = require('connect');
var app = c();

app.use("/api", function(req, res, next){
    console.log("request filter 1");
    next();
});

app.use("/api", function(req, res, next){
    console.log("request filter 2");
    next();
});

app.use("/api", function(req, res, next){
   console.log("request handler");
    res.end("hello");
    next();
});

app.use("/api", function(req, res, next){
    console.log("response post processor");
    next();
});
app.listen(3000);

When I curl for the address, I get an exception to the console complaining about headers cannot be bothered after being sent which is fair enough. Only that I do not touch the response object.

/usr/bin/node app2.js
request filter 1
request filter 2
request handler
Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:644:11)
    at ServerResponse.res.setHeader (/home/zpace/node_modules/connect/lib/patch.js:59:22)
    at next (/home/zpace/node_modules/connect/lib/proto.js:153:13)
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:25:5)
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15)
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:19:5)
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15)
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:14:5)
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15)
    at Function.app.handle (/home/zpace/node_modules/connect/lib/proto.js:198:3)

Debugging the NodeJS/Connect layer I got into a part that somehow implies that if headers are already sent then executing a route handler must initialize response headers.

The question is if the above mentioned behavior is intentional (ie that the execution of any code after a route handler has finished sending a response is something utterly unimaginable or this is simply a bug in connect?

like image 537
Peter Aron Zentai Avatar asked Jul 04 '12 20:07

Peter Aron Zentai


People also ask

Does Res send end the function?

send doesn't return the function, but does close the connection / end the request.

Can we use middleware in response in node JS?

With Node. js middleware, you can run any code and modify the request and response objects. You can also call for the next middleware in the stack when the current one is completed. The example below will help you with the process of creating your Node.

How do you end a response in node JS?

end() function is used to end the response process. This method actually comes from the Node core, specifically the response.

Can we use middleware in response?

Middleware functions can perform the following tasks: Execute any code. Make changes to the request and the response objects. End the request-response cycle.


2 Answers

Not sure whether you have found your solution.

If you want to design a post-processor for the request cycle, you can use a middleware that listens to the "finish" event on the response object. Like this:

app.use(function(req, res, next){
  res.on('finish', function(){
    console.log("Finished " + res.headersSent); // for example
    console.log("Finished " + res.statusCode);  // for example
    // Do whatever you want
  });
  next();
});

The function attached to the "finish" event will be executed after the response is written out (which means the NodeJS has handed off the response header and body to the OS for network transmission).

I guess this must be what you want.

like image 98
Weihong Diao Avatar answered Sep 27 '22 19:09

Weihong Diao


I think this is a bad planning problem. You should solve this in a better way. I dont know why you have a request handler and a request post processor separated, but lets find out what we can do.

So yes, after response has ended you cant read the headers again.

So dont finish the response until the post processor is invoked.

var isEnd;

app.use("/*", function(req, res, next){
  isEnd = false;
})

app.use("/api", function(req, res, next){
   console.log("request handler");
    res.write("hello");
    isEnd = true;
    next();
});

app.use("/api", function(req, res, next){
    console.log("response post processor");
    if(isEnd) {
        res.end();
    }
    else next();
});

This is a kind of solution, but this may not be the best for your problem.

In my opinion it is really bad that you call next() after the response has been finished. If you need a post processor, why you do that in a request filterer (or what is this). Call a function but not next()

Maybe this:

app.use("/api", function(req, res, next){
   console.log("request handler");
    res.end("hello");
    setTimeout(function(){(postProcessor(req)},0);
});

function postProcessor(req) {
//doing post process stuff.
//response not needed because already ended.
}

Or this:

app.use("/api", function(req, res, next){
   console.log("request handler");
    res.writed("hello");
    setTimeout(function(){(postProcessor(req)},0);
    // u cant res.end here because setTimeout. 
    //If you dont use setTimeout you can use res.end here, but not bot function.
});

function postProcessor(req, res) {
//doing post process stuff.
res.end();
}

The next() is not for that usage, what you uses.

I hope my answer helps you, but i know it not covers everything, but your answer is not really concrete too.

like image 31
Gergely Fehérvári Avatar answered Sep 27 '22 18:09

Gergely Fehérvári