[英]How to access final GraphQL-Reponse in nest.js with interceptor
我已经实现了一个LoggingInterceptor
,它应该能够访问最终的 GraphQL-Response 及其数据和错误属性+原始请求正文和经过身份验证的用户,该用户之前已被AuthGuard
添加到请求中。 (编辑:@jay-mcdoniel 部分解决: user
和body
可通过GqlExecutionContext.create(context).getContext()
)
事实上,拦截器只是提供了一个完全解析的 GraphQL 对象。
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(tap(
(allData) => console.log(allData),
(error)=> console.log(error)));
}
}
这是我的拦截器级。 它只是调用 RxJS-Operator tap
来记录 observable 的当前值。
如果我运行以下 GraphQL-Request ...
mutation {
login(data: { username: "admin", password: "123456" }) {
id
username
token
}
}
...我的服务器使用以下响应正文正确回答:
{
"data": {
"login": {
"id": "6f40be3b-cda9-4e6d-97ce-ced3787e9974",
"username": "admin",
"token": "someToken"
}
}
}
但是我的拦截器记录到控制台的allData
的内容如下:
{
id: '6f40be3b-cda9-4e6d-97ce-ced3787e9974',
isAdmin: true,
username: 'admin',
firstname: null,
lastname: null,
email: null,
created: 2019-07-05T15:11:31.606Z,
token: 'someToken'
}
相反,我想查看真实响应主体的信息。
我还尝试通过context.switchToHttp().getResponse()
访问 HttpResponse 。 但这仅包含mutation-login-method的参数:
{
data: [Object: null prototype] { username: 'admin', password: '123456' }
}
编辑:
console.log(GqlExecutionContext.create(context).getContext());
打印(仍然没有 GraphQL-ResponseBody):
{
headers: {
/*...*/
},
user: /*...*/,
body: {
operationName: null,
variables: {},
query: 'mutation {\n login(data: {username: "admin", password: ' +
'"123456"}) {\n token\n id\n username\n isAdmin\n }\n' +
'}\n'
},
res: ServerResponse {
_events: [Object: null prototype] { finish: [Function: bound resOnFinish] },
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
readable: true,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
writable: true,
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
timeout: 120000,
parser: [HTTPParser],
on: [Function: socketOnWrap],
_paused: false,
_httpMessage: [Circular],
[Symbol(asyncId)]: 566,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
/*...*/
},
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
connection: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
readable: true,
_events: [Object],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
writable: true,
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
timeout: 120000,
parser: [HTTPParser],
on: [Function: socketOnWrap],
_paused: false,
_httpMessage: [Circular],
[Symbol(asyncId)]: 566,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
_idleTimeout: 120000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 3273,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(asyncId)]: 567,
[Symbol(triggerId)]: 566
},
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: null,
_onPendingData: [Function: bound updateOutgoingData],
_sent100: false,
_expect_continue: false,
req: IncomingMessage {
/*...*/
},
locals: [Object: null prototype] {},
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object: null prototype] {
'x-powered-by': [Array],
'access-control-allow-origin': [Array]
}
},
_extensionStack: GraphQLExtensionStack { extensions: [ [CacheControlExtension] ] }
}
拦截器实际上是在响应之前和之后调用的,或者至少应该是这样,这样您就可以拥有请求前逻辑(请求输入)和请求后逻辑(响应输出)。 您应该能够在调用next.hanlde()
之前完成所有请求前处理,然后您应该能够在pipe()
调用之后使用RxJS Observable operators
例如tap
或map
。 您的allData
变量应该包含来自请求/响应的所有信息,您甚至可以使用context
变量来获取更多信息。
allData
目前为您打印什么? 你试过GqlExecutionContext.create(context).getContext().req
或GqlExecutionContext.create(context).getContext().res
吗? 这些显示在Guards
文档中用于获取请求和响应对象,就像使用普通 HTTP 调用一样。
这就是我在我的日志拦截器中做到的
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: Logger) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// default REST Api
if (context.getType() === 'http') {
...
...
}
// Graphql
if (context.getType<GqlContextType>() === 'graphql') {
const gqlContext = GqlExecutionContext.create(context);
const info = gqlContext.getInfo();
const res: Response = gqlContext.getContext().res;
// Get user that sent request
const userId = context.getArgByIndex(2).req.user.userId;
const parentType = info.parentType.name;
const fieldName = info.fieldName;
const body = info.fieldNodes[0]?.loc?.source?.body;
const message = `GraphQL - ${parentType} - ${fieldName}`;
// Add request ID,so it can be tracked with response
const requestId = uuidv4();
// Put to header, so can attach it to response as well
res.set('requestId', requestId);
const trace = {
userId,
body
};
this.logger.info(`requestId: ${requestId}`, {
context: message,
trace
});
return next.handle().pipe(
tap({
next: (val: unknown): void => {
this.logNext(val, context);
}
})
);
}
return next.handle();
}
/**
* Method to log response message
*/
private logNext(body: unknown, context: ExecutionContext): void {
// default REST Api
if (context.getType() === 'http') {
...
...
}
if (context.getType<GqlContextType>() === 'graphql') {
const gqlContext = GqlExecutionContext.create(context);
const info = gqlContext.getInfo();
const parentType = info.parentType.name;
const fieldName = info.fieldName;
const res: Response = gqlContext.getContext().res;
const message = `GraphQL - ${parentType} - ${fieldName}`;
// Remove secure fields from request body and headers
const secureBody = secureReqBody(body);
const requestId = res.getHeader('requestId');
// Log trace message
const trace = {
body: { ...secureBody }
};
this.logger.info(`requestId: ${requestId}`, {
context: message,
trace
});
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.