首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么res.send被叫了两次?

为什么res.send被叫了两次?
EN

Stack Overflow用户
提问于 2017-01-05 16:05:55
回答 1查看 6.6K关注 0票数 11

我正在为一个快速路由器创建一个中间件,它将为每个请求和响应执行一些代码。拦截请求很容易,并且有很多例子,但我还没有找到任何用于拦截响应的优雅方法。经过一些研究后,我想出的最好方法是替换响应对象的发送函数,如下面的代码片段所示:

代码语言:javascript
运行
复制
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");

这种方法有一个问题:由于某种原因,“拦截响应”在控制台中打印了两次,这意味着res.send被调用了两次.

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-05 17:10:30

更好的例子

试试下面的代码,看看传递给res.send的参数是什么

代码语言:javascript
运行
复制
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");
});

(我还更改了“运行”的打印,以便在服务器实际侦听时打印它--您的代码&&是在服务器侦听之前打印的--但这在这里并不重要)。

现在跑完之后:

代码语言:javascript
运行
复制
curl http://localhost:3000/api/test

服务器控制台上的输出是:

代码语言:javascript
运行
复制
Running
INTERCEPT-REQUEST
ACTUAL RESPONSE
INTERCEPT-RESPONSE {"0":{"message":"testing"}}
INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}

会发生什么?

如您所见,您的处理程序实际上被代码调用了一次,其中一个对象是第一个(也是唯一的)参数。但是,当对象序列化为JSON时,将再次调用它。这是res.send内部的工作原理--详见下文。既然您将拦截函数放在实际的响应对象上,那么我猜它是用JSON参数调用自己的,同时它甚至不知道它是否调用了您的函数。

如何避免

尝试将对象序列化为JSON:

代码语言:javascript
运行
复制
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");
});

现在它打印:

代码语言:javascript
运行
复制
Running
INTERCEPT-REQUEST
ACTUAL RESPONSE
INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}

只调用res.send一次。

解释

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

代码语言:javascript
运行
复制
  if (chunk === null) {
    chunk = '';
  } else if (Buffer.isBuffer(chunk)) {
    if (!this.get('Content-Type')) {
      this.type('bin');
    }
  } else {
    return this.json(chunk);
  }

请参阅:https://github.com/expressjs/express/blob/master/lib/response.js#L144-L154

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

但你猜怎么着-- res.json()在这一行中调用了res.send()

代码语言:javascript
运行
复制
return this.send(body);

请参阅:https://github.com/expressjs/express/blob/master/lib/response.js#L250

它在运行真正的res.send()之前调用您的拦截函数(这是第二次)。

所以谜团解开了。:)

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41489528

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档