Typescript NestJS:在异常过滤器之前执行拦截器

Typescript NestJS:在异常过滤器之前执行拦截器,typescript,nestjs,Typescript,Nestjs,我需要对某些API调用进行事件跟踪(基于处理程序名称),它基本上是一个记录活动的函数 目前,我有一个拦截器,可以跟踪这个事件,它工作得很好 但问题是,每当出现错误时,我都会使用全局异常过滤器捕获它,这会立即返回响应,而不会输入拦截器和事件跟踪代码(我需要类名、处理程序名和返回的数据(如果可用) 这是我的设置 应用程序模块.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config

我需要对某些API调用进行事件跟踪(基于处理程序名称),它基本上是一个记录活动的函数

目前,我有一个拦截器,可以跟踪这个事件,它工作得很好

但问题是,每当出现错误时,我都会使用全局异常过滤器捕获它,这会立即返回响应,而不会输入拦截器和事件跟踪代码(我需要类名、处理程序名和返回的数据(如果可用)

这是我的设置

应用程序模块.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';

import { AuthModule } from './auth/auth.module';
import { MembersModule } from './members/members.module';

import { DatabaseConfigService } from './_database/database-config.service';
import { EventTrackerService } from './event-tracker/event-tracker.service';

import { LoggingInterceptor } from './_interceptors/logging.interceptor';
import { EventsTrackerInterceptor } from './_interceptors/events-tracker.interceptor';

import { AllExceptionsFilter } from './_filters/all-exception.filter';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    MongooseModule.forRootAsync({
      useClass: DatabaseConfigService,
    }),
    AuthModule,
    MembersModule,
  ],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: EventsTrackerInterceptor,
    },
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
    EventTrackerService,
  ],
})
export class AppModule {}

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { EventTrackerService } from '../event-tracker/event-tracker.service';

@Injectable()
export class EventsTrackerInterceptor implements NestInterceptor {
  constructor(private readonly eventTrackerService: EventTrackerService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    const className = context.getClass().name;
    const handler = context.getHandler().name;

    return next.handle().pipe(
      tap(data => {
        this.eventTrackerService.handleEvent(
          request.url,
          className,
          handler,
          data,
        );
      }),
    );
  }
}

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { EventTrackerService } from '../event-tracker/event-tracker.service';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly eventTrackerService: EventTrackerService) {}
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.FORBIDDEN;

    const message =
      typeof exception.message === 'string'
        ? exception.message
        : exception.message.message;

    const errorResponse = {
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: message || 'Something went wrong',
    };
    Logger.error(
      `${request.method} ${request.url} ${status}`,
      JSON.stringify(errorResponse),
      'AllExceptionsFilter',
      true,
    );
    response.status(status).json(errorResponse);
  }
}

事件跟踪程序.interceptor.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';

import { AuthModule } from './auth/auth.module';
import { MembersModule } from './members/members.module';

import { DatabaseConfigService } from './_database/database-config.service';
import { EventTrackerService } from './event-tracker/event-tracker.service';

import { LoggingInterceptor } from './_interceptors/logging.interceptor';
import { EventsTrackerInterceptor } from './_interceptors/events-tracker.interceptor';

import { AllExceptionsFilter } from './_filters/all-exception.filter';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    MongooseModule.forRootAsync({
      useClass: DatabaseConfigService,
    }),
    AuthModule,
    MembersModule,
  ],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: EventsTrackerInterceptor,
    },
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
    EventTrackerService,
  ],
})
export class AppModule {}

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { EventTrackerService } from '../event-tracker/event-tracker.service';

@Injectable()
export class EventsTrackerInterceptor implements NestInterceptor {
  constructor(private readonly eventTrackerService: EventTrackerService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    const className = context.getClass().name;
    const handler = context.getHandler().name;

    return next.handle().pipe(
      tap(data => {
        this.eventTrackerService.handleEvent(
          request.url,
          className,
          handler,
          data,
        );
      }),
    );
  }
}

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { EventTrackerService } from '../event-tracker/event-tracker.service';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly eventTrackerService: EventTrackerService) {}
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.FORBIDDEN;

    const message =
      typeof exception.message === 'string'
        ? exception.message
        : exception.message.message;

    const errorResponse = {
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: message || 'Something went wrong',
    };
    Logger.error(
      `${request.method} ${request.url} ${status}`,
      JSON.stringify(errorResponse),
      'AllExceptionsFilter',
      true,
    );
    response.status(status).json(errorResponse);
  }
}


我的最终问题是:尽管抛出异常,我如何调用拦截器中的事件跟踪器?

拦截器有两个选项之一:1)实现
管道的
catchError
分支,执行逻辑并重新抛出错误,或者2)
点击
可以接收一个类似于订阅
的对象下一步,出错,完成
并运行某些函数来了解可观察对象的情况。水龙头可能看起来像

返回next.handle().pipe(
水龙头({
下一步:(数据)=>this.eventTrackerService.handleEvent(
request.url,
类名,
handler,
数据
),
错误:(err)=>this.eventTrackerService.handleEvent(
request.url,
类名,
handler,
错误
)
})
);

现在你有了一个
点击,它可以处理成功和错误,但是现在你也在传递错误,而不是将什么数据返回到你的跟踪器

干杯,伙计!这很有魅力。谢谢你的快速回复。