如何在调用response.end()后执行Node JS / connect中间件?

时间:2012-07-04 20:46:20

标签: javascript node.js architecture connect

我想实现这样的目标:

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);

当我为地址卷曲时,我得到一个控制台的例外,抱怨在发送之后标题不会被打扰,这是公平的。只是我不接触响应对象。

/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)

调试NodeJS / Connect层我进入了一个部分,它以某种方式暗示如果已经发送了头,那么执行路由处理程序必须初始化响应头。

问题是上述行为是否是故意的(即在路由处理程序完成发送响应之后执行任何代码是完全不可想象的,或者这只是连接中的错误?

3 个答案:

答案 0 :(得分:26)

不确定您是否找到了解决方案。

如果要为请求周期设计后处理器,可以使用侦听响应对象上的“finish”事件的中间件。像这样:

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();
});

附加到“finish”事件的函数将在写出响应后执行(这意味着NodeJS已将响应头和主体传递给OS以进行网络传输)。

我想这一定是你想要的。

答案 1 :(得分:2)

我认为这是一个糟糕的规划问题。你应该以更好的方式解决这个问题。我不知道为什么你有一个请求处理程序和请求后处理器分开,但让我们找出我们可以做什么。

所以是的,在回复结束后你再也无法读取标题了。

所以在调用后处理器之前不要完成响应。

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();
});

这是一种解决方案,但这对您的问题可能不是最好的。

在我看来,在完成回复后调用next()非常糟糕。如果您需要一个后处理器,为什么要在请求过滤器中执行此操作(或者这是什么)。调用函数但不是next()

也许这个:

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.
}

或者这个:

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();
}

next()不适用于您的使用情况。

我希望我的回答可以帮到你,但我知道它并不能涵盖所有内容,但你的答案也不是真的具体。

答案 2 :(得分:1)

尝试早晨喝咖啡是个很棒的问题!

通过proto.js查看,如果您查看第102行app.handle(中间件堆栈的处理程序代码),您将看到next()如何操作。

在调用函数next()的地方,你可以看到它检查res.headerSent是否为真,如果是,则抛出错误。

如果将第14行修改为:

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

你会看到它实际上将“headersSent”设置为true。因此,在我们结束请求之后,您可以从next()代码中看到它因为所讨论的条件而抛出错误。