我正在为一个快速路由器创建一个中间件,它将为每个请求和响应执行一些代码。拦截请求很容易,并且有很多例子,但我还没有找到任何用于拦截响应的优雅方法。经过一些研究后,我想出的最好方法是替换响应对象的发送函数,如下面的代码片段所示:
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两次?
发布于 2017-01-05 17:10:30
更好的例子
试试下面的代码,看看传递给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");
});
(我还更改了“运行”的打印,以便在服务器实际侦听时打印它--您的代码&&
是在服务器侦听之前打印的--但这在这里并不重要)。
现在跑完之后:
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);
}
请参阅:https://github.com/expressjs/express/blob/master/lib/response.js#L144-L154
您将得到else
分支,它使用您的参数调用this.json()
(实际上是res.json()
)。
但你猜怎么着-- res.json()
在这一行中调用了res.send()
:
return this.send(body);
请参阅:https://github.com/expressjs/express/blob/master/lib/response.js#L250
它在运行真正的res.send()
之前调用您的拦截函数(这是第二次)。
所以谜团解开了。:)
https://stackoverflow.com/questions/41489528
复制相似问题