关于重构的logger,如何使用AllExceptionFilter

来源:9-7 通用业务系统:日志模块代码重构(作业)

abel_meta

2023-11-29

老师,我跟着视频重构了logger模块后,发现AllExceptionFilter开始报错了,正常访问没问题,我故意弄个Exception给它就不行,比如我没登录,就会报错,以下是报错信息:

/Users/yougahei/fc-erp-gpt/node_modules/.pnpm/@nestjs+platform-express@10.2.10_@nestjs+common@10.2.10_@nestjs+core@10.2.10/node_modules/@nestjs/platform-express/adapters/express-adapter.js:28
            response.status(statusCode);
                     ^
TypeError: response.status is not a function
    at ExpressAdapter.reply (/Users/yougahei/fc-erp-gpt/node_modules/.pnpm/@nestjs+platform-express@10.2.10_@nestjs+common@10.2.10_@nestjs+core@10.2.10/node_modules/@nestjs/platform-express/adapters/express-adapter.js:28:22)
    at AllExceptionFilter.catch (/Users/yougahei/fc-erp-gpt/apps/biz-server/src/filters/all-exception.filters.ts:49:21)
    at ExceptionsHandler.invokeCustomFilters (/Users/yougahei/fc-erp-gpt/node_modules/.pnpm/@nestjs+core@10.2.10_@nestjs+common@10.2.10_@nestjs+platform-express@10.2.10_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/exceptions/exceptions-handler.js:30:26)
    at ExceptionsHandler.next (/Users/yougahei/fc-erp-gpt/node_modules/.pnpm/@nestjs+core@10.2.10_@nestjs+common@10.2.10_@nestjs+platform-express@10.2.10_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/exceptions/exceptions-handler.js:14:18)
    at /Users/yougahei/fc-erp-gpt/node_modules/.pnpm/@nestjs+core@10.2.10_@nestjs+common@10.2.10_@nestjs+platform-express@10.2.10_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/router/router-proxy.js:13:35
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

main.ts

import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app/app.module';
import type {
    CorsConfig,
    NestConfig,
    SwaggerConfig,
} from './common/configs/config.interface';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import 'winston-daily-rotate-file';
import { AllExceptionFilter } from './filters/all-exception.filters';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    const logger = app.get(WINSTON_MODULE_NEST_PROVIDER);
    app.useLogger(logger);

    const globalPrefix = 'api';
    app.setGlobalPrefix(globalPrefix);

    // Validation
    app.useGlobalPipes(new ValidationPipe());

    // enable shutdown hook
    app.enableShutdownHooks();

    // Prisma Client Exception Filter for unhandled exceptions
    const httpAdapter = app.get(HttpAdapterHost);
    // app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter));
    app.useGlobalFilters(new AllExceptionFilter(logger, httpAdapter));

    const configService = app.get(ConfigService);
    const nestConfig = configService.get<NestConfig>('nest');
    const corsConfig = configService.get<CorsConfig>('cors');
    const swaggerConfig = configService.get<SwaggerConfig>('swagger');

    // Swagger Api
    if (swaggerConfig.enabled) {
        const options = new DocumentBuilder()
            .setTitle(swaggerConfig.title || 'Nestjs')
            .setDescription(
                swaggerConfig.description || 'The nestjs API description'
            )
            .setVersion(swaggerConfig.version || '1.0')
            .build();
        const document = SwaggerModule.createDocument(app, options);

        SwaggerModule.setup(swaggerConfig.path || 'api', app, document);
    }

    // Cors
    if (corsConfig.enabled) {
        app.enableCors();
    }

    await app.listen(process.env.PORT || nestConfig.port || 3000);
}

bootstrap();

all-exception.filters.ts

import {
    ArgumentsHost,
    Catch,
    ExceptionFilter,
    HttpException,
    HttpStatus,
    Inject,
    LoggerService,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import * as requestIp from 'request-ip';

@Catch()
export class AllExceptionFilter implements ExceptionFilter {
    constructor(
        @Inject(WINSTON_MODULE_NEST_PROVIDER)
        private readonly logger: LoggerService,
        private readonly httpAdapterHost: HttpAdapterHost
    ) {}

    catch(exception: unknown, host: ArgumentsHost) {
        const { httpAdapter } = this.httpAdapterHost;
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();

        const request = ctx.getRequest();

        const httpStatus =
            exception instanceof HttpException
                ? exception.getStatus()
                : HttpStatus.INTERNAL_SERVER_ERROR;

        const msg: unknown = exception['response'] || 'Internal Server Error';

        const responseBody = {
            headers: request.headers,
            query: request.query,
            body: request.body,
            params: request.params,
            timestamp: new Date().toISOString(),
            // 还可以加入一些用户信息
            // IP信息
            ip: requestIp.getClientIp(request),
            exceptioin: exception['name'],
            error: msg,
        };
        this.logger.error('[tomic]', responseBody);
        httpAdapter.reply(responseBody, response, httpStatus);
    }
}

Http-exception.filters.ts

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  Inject,
  LoggerService,
} from '@nestjs/common';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  constructor(
    @Inject(WINSTON_MODULE_NEST_PROVIDER)
    private logger: LoggerService,
  ) {}

  catch(exception: HttpException, host: ArgumentsHost) {
    //  获取上下文
    const ctx = host.switchToHttp();
    console.log(ctx);
    // 获取响应对象
    const response = ctx.getResponse<Response>();
    console.log(response);

    // 获取请求对象
    const request = ctx.getRequest<Request>();
    // 获取状态码
    const status = exception.getStatus();

    // 记录日志
    this.logger.error(exception.message, exception.stack);

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      //   path: request.url,
      //   method: request.method,
      message: exception.message || exception.name,
    });

    // throw new Error('Method not implemented.');
  }
}

写回答

2回答

Brian

2023-12-01

根据 NestJS 文档,reply 方法的第一个参数应该是原始的响应对象(通常是 Express 的 Response 对象),第二个参数是您想要发送的响应体,第三个参数是响应状态码。


试试下面的代码:

catch(exception: unknown, host: ArgumentsHost) {
    const { httpAdapter } = this.httpAdapterHost;
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const httpStatus =
        exception instanceof HttpException
            ? exception.getStatus()
            : HttpStatus.INTERNAL_SERVER_ERROR;

    const msg: unknown = exception['response'] || 'Internal Server Error';

    const responseBody = {
        // ... 其他响应体内容
    };
    this.logger.error('[tomic]', responseBody);

    // 注意这里的参数顺序
    httpAdapter.reply(response, responseBody, httpStatus);
}


1
0

Brian

2023-12-01

at ExpressAdapter.reply  错误出在这里,说明传递给httpAdapter.reply的response对象,可能不是express对象,你可以单步调试一下

1
0

NestJS 入门到实战 前端必学服务端新趋势

近几年快速发展的Node.js框架,掌握未来前端工程师后端开发能力

569 学习 · 238 问题

查看课程