Skip to content

异常过滤器和日志

抛出异常

抛出标准异常

ts
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);

throw new HttpException({
  status: HttpStatus.FORBIDDEN,
  error: 'This is a custom message',
}, HttpStatus.FORBIDDEN, {
  cause: error
});

抛出自定义异常

ts
// 定义异常
export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}
// 使用
threw new ForbiddenException();

异常过滤器

异常过滤器是处理异常的,当抛出异常时,会调用异常过滤器。

定义异常处理逻辑

common/filters/AllException.filters.ts
ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch() // 👈 不传参数,表示捕获所有异常
export class AllExceptionFilter implements ExceptionFilter {
  catch(exception: Error, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status = HttpStatus.INTERNAL_SERVER_ERROR;
    const message = exception.message || '服务器错误';

    // 返回标准格式
    response.status(status).json({
      code: status,
      message,
      path: request.url,
    });
  }
}
common/filters/HttpException.filters.ts
ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException) // 👈 只捕获 HttpException 及其子类
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status = exception.getStatus() || HttpStatus.INTERNAL_SERVER_ERROR;

    // Nest 内置异常结构中 getResponse() 可能返回 string 或 object
    const exceptionResponse = exception.getResponse();
    const message =
      typeof exceptionResponse === 'string'
        ? exceptionResponse
        : (exceptionResponse as { message: string }).message ||
          exception.message;

    // 返回统一格式
    response.status(status).json({
      code: status,
      message,
      path: request.url,
      params: request.params,
    });
  }
}

绑定过滤器

user.controller.ts

ts
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}

全局作用域

main.ts

ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

日志

在 main.ts 中开启日志

ts
const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn'],
});

在类中使用

ts
export class UserService {
  private readonly logger = new Logger(UserService.name);

   findAll() {
    this.logger.error(12);
  }
}

自定义日志

common/logger/logger.ts

ts
import { LoggerService, Injectable, LogLevel } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class MyLogger implements LoggerService {
  private getTimestamp(): number {
    return new Date().getTime();
  }
  private getContext(...rest: any[]): string {
    let context = '';
    if (typeof rest === 'object' && !(rest instanceof Array)) {
      context = rest;
    } else {
      context = rest.toString();
    }
    return context;
  }

  getReqInfo = (request: Request) => {
    const reqInfo: Record<string, any> = {};
    reqInfo.url = request.url;
    reqInfo.method = request.method;
    reqInfo.ip = request.ip;
    if (request.body !== undefined) {
      reqInfo.body = JSON.stringify(request.body);
    }
    if (request.params !== undefined) {
      reqInfo.params = JSON.stringify(request.params);
    }
    if (request.query !== undefined) {
      reqInfo.query = JSON.stringify(request.query);
    }
    if (reqInfo.ip === '::1') {
      reqInfo.ip = '127.0.0.1';
    }
    return reqInfo;
  };
  private getLogger = (
    level: LogLevel,
    message: string,
    request: Request,
    ...rest: any[]
  ) => {
    const timestamp = this.getTimestamp();
    const context = this.getContext(rest);
    const reqInfo = this.getReqInfo(request);
    return {
      level,
      timestamp,
      context,
      message,
      ...reqInfo,
    };
  };

  log(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('log', message, request, rest));
  }
  error(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('error', message, request, rest));
  }
  warn(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('warn', message, request, rest));
  }
  debug?(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('debug', message, request, rest));
  }
  verbose?(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('verbose', message, request, rest));
  }
  fatal?(message: string, request: null | Request, ...rest: any[]) {
    console.log(this.getLogger('fatal', message, request, rest));
  }
}

main.ts

ts
import { MyLogger } from './common/logger/logger.service';

const app = await NestFactory.create(AppModule, {
  logger: new MyLogger(),
});