Node.js 为什么res.send会被调用两次?

Node.js 为什么res.send会被调用两次?,node.js,express,Node.js,Express,我正在为express路由器创建一个中间件,它将为每个请求和响应执行一些代码。拦截请求很容易,而且有很多例子,但我还没有找到任何优雅的方法来拦截响应。经过一些研究,我想到的最好办法是替换response对象的send函数,如以下代码段所示: const express = require('express'); const app = express(); var router = express.Router(); var port = process.env.PORT || 3000; r

我正在为express路由器创建一个中间件,它将为每个请求和响应执行一些代码。拦截请求很容易,而且有很多例子,但我还没有找到任何优雅的方法来拦截响应。经过一些研究,我想到的最好办法是替换response对象的send函数,如以下代码段所示:

const express = require('express');
const app = express();
var router = express.Router();
var port = process.env.PORT || 3000;

router.get('/test', function(req, res) {
    res.send({
        message: "testing"
    });
});

app.use(function(req, res, next){
    console.log("INTERCEPT-REQUEST");
    const orig_send = res.send;
    res.send = function(arg) {
        console.log("INTERCEPT-RESPONSE");
        orig_send.call(res, arg);
    };
    next();
});

app.use("/api", router);
app.listen(process.env.PORT || 3000) && console.log("Running");
这种方法有一个问题:出于某种原因,“INTERCEPT-RESPONSE”在控制台中打印了两次,这意味着res.send被调用了两次

我可以在res.locals第一次被调用时设置一个标志,以避免处理两次响应,但我想知道为什么res.send会被调用两次?

更好的例子 尝试此代码以查看传递给
res.send
的参数:

const express = require('express');
const app = express();
var router = express.Router();
var port = process.env.PORT || 3000;

router.get('/test', function(req, res) {
    console.log('ACTUAL RESPONSE');
    res.send({
        message: "testing"
    });
});

app.use(function(req, res, next){
    console.log("INTERCEPT-REQUEST");
    const orig_send = res.send;
    res.send = function(arg) {
        console.log("INTERCEPT-RESPONSE", JSON.stringify(arguments));
        orig_send.call(res, arg);
    };
    next();
});

app.use("/api", router);
app.listen(process.env.PORT || 3000, function () {
    console.log("Running");
});
(我还将“Running”的打印更改为在服务器实际侦听时打印-您的代码
&&
是在服务器侦听之前打印的-但这在这里并不重要)

现在运行后:

curl http://localhost:3000/api/test
服务器控制台上的输出为:

Running
INTERCEPT-REQUEST
ACTUAL RESPONSE
INTERCEPT-RESPONSE {"0":{"message":"testing"}}
INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}
发生了什么 正如您所看到的,您的代码实际上只调用了一次处理程序,第一个(也是唯一一个)参数是一个对象。但是,它再次被一个序列化为JSON的对象调用。这就是
res.send
内部工作原理-详情见下文。既然你把截取函数放在了实际的响应对象上,那么我猜它是在用JSON参数调用自己,而它甚至不知道它同时调用了你的函数

如何避免 请尝试使用自己序列化为JSON的对象:

const express = require('express');
const app = express();
var router = express.Router();
var port = process.env.PORT || 3000;

router.get('/test', function(req, res) {
    console.log('ACTUAL RESPONSE');
    res.send(JSON.stringify({
        message: "testing"
    }));
});

app.use(function(req, res, next){
    console.log("INTERCEPT-REQUEST");
    const orig_send = res.send;
    res.send = function(arg) {
        console.log("INTERCEPT-RESPONSE", JSON.stringify(arguments));
        orig_send.call(res, arg);
    };
    next();
});

app.use("/api", router);
app.listen(process.env.PORT || 3000, function () {
    console.log("Running");
});
现在它打印:

Running
INTERCEPT-REQUEST
ACTUAL RESPONSE
INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}
只调用
res.send
一次

解释 现在,这是处理
res.json
的对象参数的代码:

  if (chunk === null) {
    chunk = '';
  } else if (Buffer.isBuffer(chunk)) {
    if (!this.get('Content-Type')) {
      this.type('bin');
    }
  } else {
    return this.json(chunk);
  }
见:

您得到了
else
分支,它使用您的参数调用
this.json()
(实际上是
res.json()

但是猜猜看-
res.json()
在这一行调用了
res.send()

return this.send(body);
见:

它在运行real
res.send()
之前(第二次)调用拦截函数


所以,谜团解开了。:)

请注意,您如何调用send from
orig_send
是错误的。它应该是:
orig_send.call(res,arg)
,因为
send()
res
的一种方法,感谢@slebetman进行了更新,但同样的行为也发生了。我遇到了这个问题。我改用了
res.end
,因为这只调用了一次。谢谢@rsp的精彩解释,现在完全有意义了!