我想记录每个请求的响应信息。
我尝试使用中间件,但是有问题。
res.body
未定义。
app.use((req, res, next) => {
var time = Date.now();
res.on('finish', function() {
var clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
var method = req.method;
var path = req.baseUrl;
var duration = Date.now() - time;
console.log({
clientIp,
elapsedTime: `${duration}ms`,
hostname: req.headers.host,
level: 'INFO',
method,
path,
phase: process.env.NODE_ENV,
reqBody: req.body,
reqHeaders: req.headers,
resBody: res.body,
resHeaders: res.getHeaders(),
status: res.statusCode
});
});
next();
});
实际上,客户端上有响应数据。
如何在中间件中获取响应正文?
答案 0 :(得分:2)
可以通过重写response.json
函数来截获响应。这样,并添加我们的custom function
,每次调用response.json()
时,都会触发我们的拦截功能。
middleware / response.filter.js:
// Response Interceptor Middleware
export default (request, response, next) => {
try {
const oldJSON = response.json;
response.json = (data) => {
// For Async call, handle the promise and then set the data to `oldJson`
if (data && data.then != undefined) {
// Resetting json to original to avoid cyclic call.
return data.then((responseData) => {
// Custom logic/code.
response.json = oldJSON;
return oldJSON.call(response, responseData);
}).catch((error) => {
next(error);
});
} else {
// For non-async interceptor functions
// Resetting json to original to avoid cyclic call.
// Custom logic/code.
response.json = oldJSON;
return oldJSON.call(response, finalResponse);
}
}
} catch (error) {
next(error);
}
}
在Server.js
文件中,注册中间件:
// Server.js file
import externalResponseFilter from "./middleware/response.filter.js:";
// Create Express server
const app = express();
// Response interceptor - Initialization.
app.use(externalResponseFilter);
在要返回response
的控制器中,使用response.json()
函数而不是response.send()
返回。
让我知道是否需要其他说明。
答案 1 :(得分:0)
Express中的响应对象只是节点的http.ServerResponse
类。它是可写的Stream,因此您可以将其作为Stream进行交互。它还通过net.Socket
公开了基础res.socket
连接。
这是有趣的地方。因为net.Socket
是普通可写流。如果您使用.write()
方法覆盖它,则将能够拦截从套接字到网络的所有字节:
function interceptorMiddleware (req, res, next) {
const sock = req.socket;
const write = sock.write.bind(sock);
sock.write = (data, encoding, callback) => {
console.log(data);
write(data, encoding, callback);
}
sock.on('close', () => console.log('DONE'));
next();
}
您需要在应用程序中尽早安装此中间件,然后才能有任何机会写入套接字。
由于节点的异步特性,您可能会在日志输出中得到混合的数据包,因此可能值得添加一些代码来标识哪个套接字正在生成输出:
console.log(sock.remoteAddress, sock.remotePort, data);
警告!
console.log()
在输出到终端或文件时是同步的。 这样做可能会严重降低服务器的性能。 如果您将stdout用管道传输到另一个进程(例如pm2),那还不错 因为它将是异步的,但仍可能会给您带来轻微的性能损失。将此用于调试仅
答案 2 :(得分:0)
截取来自 res.json
的响应正文并将其存储在 res.locals
中以供稍后访问。
app.express.use((req, res, next) => {
const oldJson = res.json;
res.json = (body) => {
res.locals.body = body;
return oldJson.call(res, body);
};
next();
});