Javascript nestjs中间件获取请求/响应正文
我正在为一个项目使用nestjs,并希望记录尽可能多的信息,其中之一就是每个http请求的响应和请求的主体。我为此做了一个嵌套中间件:Javascript nestjs中间件获取请求/响应正文,javascript,node.js,json,typescript,nestjs,Javascript,Node.js,Json,Typescript,Nestjs,我正在为一个项目使用nestjs,并希望记录尽可能多的信息,其中之一就是每个http请求的响应和请求的主体。我为此做了一个嵌套中间件: import {token} from 'gen-uid'; import { inspect } from 'util'; import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common'; import { Stream } from 'stream'; impor
import {token} from 'gen-uid';
import { inspect } from 'util';
import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common';
import { Stream } from 'stream';
import { createWriteStream, existsSync, mkdirSync } from 'fs';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
logfileStream: Stream;
constructor() {
if (!existsSync('./logs')) mkdirSync('./logs');
this.logfileStream = createWriteStream("./logs/serviceName-"+ new Date().toISOString() + ".log", {flags:'a'});
}
resolve(...args: any[]): MiddlewareFunction {
return (req, res, next) => {
let reqToken = token();
let startTime = new Date();
let logreq = {
"@timestamp": startTime.toISOString(),
"@Id": reqToken,
query: req.query,
params: req.params,
url: req.url,
fullUrl: req.originalUrl,
method: req.method,
headers: req.headers,
_parsedUrl: req._parsedUrl,
}
console.log(
"timestamp: " + logreq["@timestamp"] + "\t" +
"request id: " + logreq["@Id"] + "\t" +
"method: " + req.method + "\t" +
"URL: " + req.originalUrl);
this.logfileStream.write(JSON.stringify(logreq));
const cleanup = () => {
res.removeListener('finish', logFn)
res.removeListener('close', abortFn)
res.removeListener('error', errorFn)
}
const logFn = () => {
let endTime = new Date();
cleanup()
let logres = {
"@timestamp": endTime.toISOString(),
"@Id": reqToken,
"queryTime": endTime.valueOf() - startTime.valueOf(),
}
console.log(inspect(res));
}
const abortFn = () => {
cleanup()
console.warn('Request aborted by the client')
}
const errorFn = err => {
cleanup()
console.error(`Request pipeline error: ${err}`)
}
res.on('finish', logFn) // successful pipeline (regardless of its response)
res.on('close', abortFn) // aborted pipeline
res.on('error', errorFn) // pipeline internal error
next();
};
}
}
然后我将这个中间件设置为一个全局中间件来记录所有请求,但是查看res和req对象,它们都没有属性
在代码示例中,我将响应对象设置为打印,在我的项目上运行helloworld端点,该端点返回{“message”:“helloworld”}
我得到以下输出:
时间戳:2019-01-09T00:37:00.912Z请求id:2852f925f987方法:获取URL:/hello world
服务器响应{
域:空,
_事件:{finish:[函数:绑定重新配置]},
_事件提示:1,
_maxListeners:未定义,
输出:[],
输出编码:[],
outputCallbacks:[],
输出大小:0,
可写:对,
_最后:错,
升级:错,
错误:错误,
真的,
useChunkedEncodingByDefault:true,
发送日期:对,
_removedConnection:false,
_removedContLen:是的,
_雷蒙维特:是的,
_contentLength:0,
_哈斯博迪:错,
_预告片:'',
完成:对,
_海德森:是的,
套接字:null,
连接:空,
_标题:“HTTP/1.1 304未修改\r\nX由以下驱动:Express\r\nETag:W/“19-c6Hfa5VVP+Ghysj+6y9cPi5QQbk”\r\n日期:Wed,2019年1月9日00:37:00 GMT\r\n连接:保持活动\r\n\r\n”,
_onPendingData:[函数:绑定updateOutgoingData],
_第100句:错,
_期望继续:false,
请求:
收入信息{
_可读状态:
可读状态{
objectMode:false,
高水印:16384,
缓冲区:[对象],
长度:0,
管道:null,
管道:0,
是的,
结束:对,
提交:错误,
阅读:错,
是的,
答案:错,
对,对,
可读性听力:错误,
简历:对,
销毁:错误,
defaultEncoding:'utf8',
时间:0,,
雷丁摩尔:没错,
解码器:空,
编码:null},
可读性:对,
域:空,
_事件:{},
_事件提示:0,
_maxListeners:未定义,
插座:
插座{
连接:错,
_haderro:false,
_句柄:[对象],
_父项:null,
_主机:空,
_readableState:[对象],
可读性:对,
域:空,
_事件:[对象],
_活动日期:10,,
_maxListeners:未定义,
_writableState:[对象],
可写:对,
AllowAlfOpen:是的,
_字节:155,
_sockname:null,
_pendingData:null,
_彭丁编码:'',
服务器:[对象],
_服务器:[对象],
_空闲时间:5000,
_idleNext:[对象],
_idlePrev:[对象],
_idleStart:12562,
_销毁:错误,
解析器:[对象],
on:[函数:socketOnWrap],
_暂停:错,
读:[函数],
_消费:是的,
_httpMessage:null,
[符号(异步ID)]:151,
[符号(字节读取)]:0,
[符号(异步ID)]:153,
[符号(triggerAsyncId)]:151},
连接:
插座{
连接:错,
_haderro:false,
_句柄:[对象],
_父项:null,
_主机:空,
_readableState:[对象],
可读性:对,
域:空,
_事件:[对象],
_活动日期:10,,
_maxListeners:未定义,
_writableState:[对象],
可写:对,
AllowAlfOpen:是的,
_字节:155,
_sockname:null,
_pendingData:null,
_彭丁编码:'',
服务器:[对象],
_服务器:[对象],
_空闲时间:5000,
_idleNext:[对象],
_idlePrev:[对象],
_idleStart:12562,
_销毁:错误,
解析器:[对象],
on:[函数:socketOnWrap],
_暂停:错,
读:[函数],
_消费:是的,
_httpMessage:null,
[符号(异步ID)]:151,
[符号(字节读取)]:0,
[符号(异步ID)]:153,
[符号(triggerAsyncId)]:151},
httpVersionMajor:1,
httpVersionMinor:1,
httpVersion:'1.1',
完全正确,
标题:
{host:'localhost:5500',
“用户代理”:“Mozilla/5.0(X11;Ubuntu;Linux x86_64;rv:64.0)Gecko/20100101 Firefox/64.0”,
accept:'text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8',
‘接受语言’:‘en-US,en;q=0.5’,
“接受编码”:“gzip,deflate”,
连接:“保持活力”,
“升级不安全请求”:“1”,
“如果不匹配”:“W/”19-c6Hfa5VVP+Ghysj+6y9cPi5QQbk”,
原始标题:
[“主机”,
'本地主机:5500',
“用户代理”,
“Mozilla/5.0(X11;Ubuntu;Linux x86_64;rv:64.0)Gecko/20100101 Firefox/64.0”,
“接受”,
'text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8',
“接受语言”,
‘en-US,en;q=0.5’,
“接受编码”,
“gzip,deflate”,
“连接”,
"活下去",,
“升级不安全的请求”,
'1',
'如果没有匹配',
“W/“19-c6Hfa5VVP+Ghysj+6Y9CPI5QBK”],
预告片:{},
Raw:[],
升级:false,
url:“/hello world”,
方法:“GET”,
状态代码:null,
statusMessage:null,
客户:
插座{
连接:错,
_遮光罩
var mung = require('express-mung');
app.use(mung.json(
function transform(body, req, res) {
console.log(body); // or whatever logger you use
return body;
}
));
providers: [
{
provide: APP_INTERCEPTOR,
useClass: HttpInterceptor,
}
]
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class HttpInterceptor implements NestInterceptor {
private readonly logger = new Logger(HttpInterceptor.name);
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
return next.handle().pipe(
map(data => {
this.logger.debug(data);
return data;
}),
);
}
}