【问题标题】:How to have a NodeJS/connect middleware execute after responde.end() has been invoked?如何在调用 response.end() 后执行 Node JS/connect 中间件?
【发布时间】:2014-02-18 23:30:15
【问题描述】:

我想实现这样的目标:

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 层我进入了一个以某种方式暗示如果标头已发送,则执行路由处理程序必须初始化响应标头的部分。

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

【问题讨论】:

  • 您在代码中使用res.end("hello")
  • 是的。响应处理完成,响应准备发送。现在我想放置一个日志或清理一些东西。
  • 自从你问了这个问题后,你有没有找到办法做到这一点?这个帖子似乎没有任何回应,我试图理解为什么 Connect 团队会按照他们的方式实施。
  • 不,我没有找到任何解决方案,所以选择退出所需的功能。

标签: javascript node.js architecture connect


【解决方案1】:

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

如果你想为请求周期设计一个后处理器,你可以使用一个中间件来监听响应对象上的“完成”事件。像这样:

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已将响应头和响应体交给操作系统进行网络传输)。

我想这一定是你想要的。

【讨论】:

  • finish 事件不会被触发,以防响应对象已经触发了close 事件 - 它发生在套接字在发送响应之前关闭时。如果您想捕捉请求/响应周期结束的时刻(即使错误处理中间件发送响应),最好覆盖响应的end 函数:var _end = res.end; res.end = function(){ console.log('the very end of the response'); _end.apply(this, arguments); }
【解决方案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() 不适用于您使用的那种用途。

希望我的回答对你有所帮助,但我知道它不能涵盖所有内容,但你的回答也不是很具体。

【讨论】:

  • 考虑 nodejs/connect 层填充了来自各种来源的混合逻辑:A) 代表业务逻辑的代码和 B) 代表基础设施的代码。因此,如果两个不同的 ppl 创建了业务和基础设施部分,那么这里就不能进行合作/强耦合的功能开发。业务逻辑人员必须像独自运行(不知道基础架构)一样处理事情,基础架构代码也需要同样的事情:与业务层代码无关。
  • 再说一遍:在最后一个功能(“后处理器”)中,我不会触及任何已冻结的标题、正文。
【解决方案3】:

用早晨的咖啡来锻炼身体真是个好问题!

所以查看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() 代码中看到,由于所讨论的条件,它会抛出错误。

【讨论】:

  • 是的,但是发出请求并不一定意味着我们已经完成了请求周期。恕我直言,这只是一种可能的方法。
猜你喜欢
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多